一、在編譯一個使用了CPerson類的文件時,若是不須要知道該類的所有細節,只須要知道有一個類名叫CPerson,最好「向前聲明」該類(同理於對一個協議的引用),這樣作也能夠避免「循環引用」問題:objective-c
例:編程
@class CPerson多線程
例:框架
#import "CShape.h"函數
#import "CDrawable.h"性能
@interface CRectangle : CShape <CDrawable>atom
二、多用字面量語法,少用與之等價的方法 (NSString/NSNumber/NSArray/NSDictionary)spa
字面量語法的限制:除了字符串之外,所建立出來的對象必須屬於Foundation框架才行。若是自定義了這些類的之類,則沒法用字面量語法建立其對象。.net
例:線程
NSString *someString = @"Effective Objective-C 2.0";
NSNumber *someNumber = @1 ;
NSNumber *floatNumber = @2.5f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
NSArray *animals = @[@"cat", @"dog",@"pig"];//若@「dog」爲空,程序會拋出異常令應用程序終止執行,達到檢查是否含空字符串的目的
NSArray *dog = animals[1];
NSDictionary *personData = @{@"firstName" : @"Matt", @"lastName" : @"Galloway", @"age" : @28};//一旦有值爲nil也會拋出異常
NSDictionary *firstName = personData[@"firstName"];
三、多用類型常量const static,少用#define預處理指令,這樣建立出來的常量有類型信息,便於差錯。
1)若不打算公開某個常量,則應將其定義在使用該常量的實現文件裏;
2)在頭文件中使用extern來聲明全局常量,並在相關實現文件中定義其值。
四、用枚舉表示狀態、選項、狀態碼
enum Week {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
enum Week day = Monday;
五、理解「屬性」 (屬性用於封裝對象中的數據,保存着數據的實例變量通常經過「存取方法」來訪問)
1)atomic(原子性),若是屬性具有nonatomic特質,則不使用同步鎖;(開發iOS程序中全部的屬性都聲明爲nonatomic,緣由是:在iOS中使用同步鎖的開銷較大,這會帶來性能問題;可是在開發Mac OS X程序時,使用atomic屬性一般都不會有性能瓶頸。因此在iPhone這種小型設備上,若是沒有使用多線程間的通信編程,那麼nonatomic是一個很是好的選擇。)
2)readwrite(讀寫),擁有「獲取方法」和「設置方法」,由@synthesize實現;
3)readonly(只讀),僅擁有「獲取方法」,由@synthesize實現;
4)assign,「設置方法」只會執行鍼對「純量類型」(CGFloat、NSInteger等)的簡單賦值操做,不更改索引計數,即setter方法直接賦值,不進行任何retain操做(對基礎數據類型 (NSInteger)和C數據類型(int, float, double, char, 等)使用,不然可能致使內存泄露)
5)strong,設置方法會保留新值,並釋放舊值,而後再將新值設置上去;使用ARC機制時,等同於retain
6)weak,設置方法既不保留新值,也不釋放舊值。此特質同assign相似;使用ARC機制時,等同於assign,對象被釋放時,屬性的值會被設置爲nil
7)copy,設置方法並不保留新值,而是將其「拷貝」,創建一個索引計數爲1的對象,而後釋放舊對象,即setter方法進行Copy操做,與retain處理流程同樣,先舊值release,再Copy出新的對象,retainCount爲1,爲了減小對上下文的依賴而引入的機制(對NSString )
例:-(void)setOne:(NSObject *) other
{
if(one != other)
{
[one release];
one = [other copy];
}
}
8)retain,釋放舊的對象,將舊對象的值賦予輸入對象,再提升輸入對象的索引計數爲1,即setter方法對參數進行release舊值再retain新值,爲了解決原類型與環循引用問題(對其餘NSObject和其子類)
例:-(void)setOne:(NSObject *) other
{
if(one != other)
{
[one release];
one = [other retain];
}
}
(copy與retain的區別:
copy是建立一個新對象,retain是建立一個指針,引用對象計數加1
copy 到另一個NSString 以後,地址爲不一樣 ,內容相同,新的對象retain爲1 ,舊有對象沒有變化
retain 到另一個NSString 以後,地址相同(創建一個指針,指針拷貝),內容固然相同,這個對象的retain值+1)
a、property,他能夠提供的功能有:提供成員變量的訪問方法的聲明、控制成員變量的訪問權限、控制多線程時成員變量的訪問環境
b、synthesize的理解是:實現property所聲明的方法的定義
c、使用屬性的話,編譯器會自動生成存取方法,並自動向類中添加適當類型的實例變量,並在屬性名前面加下劃線。
d、可用@synthesize語法來指定實例變量的名字;用@dynamic關鍵字則會告訴編譯器:不要自動建立實現屬性所用的實例變量,也不要爲其建立存取方法。
六、在對象內部儘可能直接訪問實例變量,在對象以外訪問實例變量時老是經過屬性來作
(筆者強烈建議在讀取實例變量的時候採用直接訪問的形式,而在設置實例變量的時候經過屬性來作)
(在初始化方法及dealloc方法中,老是應該直接經過實例變量來讀寫數據)
直接訪問(_name) 與 屬性訪問(self.name) 的區別:
1)直接訪問速度快,編譯器所生成的代碼會直接訪問保存對象實例變量的那塊內存;
2)直接訪問不會調用「設置方法」,這就繞過了爲相關屬性所定義的「內存管理語義」;
3)直接訪問實例變量不會觸發「鍵值觀測」(KVO)通知;
4)經過屬性來訪問有助於排查與之相關的錯誤,由於能夠給「設置方法」設置斷點。
七、理解「對象等同性」這一律念
1)按照 == 操做符比較的是兩個指針自己,而不是其所指的對象;
2)應該使用NSObject協議中聲明的「isEqual」:方法來判斷兩個對象的等同性。(「isEqualToString:」、「isEqualToArray:」、「isEqualToDictionary:」)
3)相同的對象必須具備相同的哈希碼,可是兩個哈希碼相同的對象卻未必相同。
八、instancetype和id的異同
1)相同點:
均可以做爲方法的返回類型
2)不一樣點:
instancetype能夠返回和方法所在類相同類型的對象,id只能返回未知類型的對象;
instancetype只能做爲返回值,不能像id那樣做爲參數。
九、@selector
1)一種類型 SEL
2)表明你要發送的消息(方法), 跟字符串有點像, 也能夠互轉.: NSSelectorFromString() / NSSelectorFromString()
3)能夠理解爲相似函數指針的東西–是能讓Objective-C動態調用方法的玩意.–是 object-c 的動態後綁定技術 能夠經過字符串 訪問的函數指針
4)其實就是消息響應函數—選一個消息響應的函數地址給你的action
5)@selector(function_name) 即取得一個function的id
十、理解objc_msgSend的做用(「傳遞消息」)
1)消息由接收者、選擇子(方法)及參數構成。給某對象「發送消息」也就是至關於在該對象上「調用方法」;
2)發給某對象的所有消息都要由「動態消息派發系統」來處理,該系統會查出對應的方法,並執行其代碼;
3)C語言使用「靜態綁定」(編譯期就能決定運行時所應調用的函數),而objective-c則使用「動態綁定」(所要調用的函數直到運行期才能肯定)
4)編譯器看到消息後,會將其轉換爲一條標準的C語言函數調用,這個核心函數叫作:objc_msgSend:
id returnValue = [someObject messageName:parameter];
==> id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);
(該方法須要在接收者所屬的類中搜尋其「方法列表」,若是能找到與選擇子名稱相符的方法,就跳至其實現代碼。如果找不到,那就沿着繼承體系繼續向上查找,等找到合適的方法以後再跳轉。若是最終仍是找不到相符的方法,那就執行「消息轉發」操做。)
十一、@synthesize和 @dynamic
@property有兩個對應的詞,一個是@synthesize,一個是@dynamic。若是@synthesize和@dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
@synthesize的語義是若是你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法。
@dynamic告訴編譯器,屬性的setter與getter方法由用戶本身實現,不自動生成。(固然對於readonly的屬性只需提供getter便可)。假如一個屬性被聲明爲@dynamic var,而後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,可是當程序運行到instance.var =someVar,因爲缺setter方法會致使程序崩潰;或者當運行到 someVar = var時,因爲缺getter方法一樣會致使崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。
十二、全局變量,和局部變量再內存佔用上有什麼區別?
變量能夠分爲:全局變量、靜態全局變量、靜態局部變量和局部變量。 按存儲區域分,全局變量、靜態全局變量和靜態局部變量都存放在內存的靜態存儲區域,局部變量存放在內存的棧區。
按做用域分,全局變量在整個工程文件內都有效;靜態全局變量只在定義它的文件內有效;靜態局部變量只在定義它的函數內有效,只是程序僅分配一次內存,函數返回後,該變量不會消失;局部變量在定義它的函數內有效,可是函數返回後失效。
全局變量和靜態變量若是沒有手工初始化,則由編譯器初始化爲0。局部變量的值不可知。
靜態全局變量,只本文件能夠用。
全局變量是沒有定義存儲類型的外部變量,其做用域是從定義點到程序結束.省略了存儲類型符,系統將默認爲是自動型.
靜態全局變量是定義存儲類型爲靜態型的外部變量,其做用域是從定義點到程序結束,所不一樣的是存儲類型決定了存儲地點,靜態型變量是存放在內存的數據區中的,它們在程序開始運行前就分配了固定的字節,在程序運行過程當中被分配的字節大小是不改變的.只有程序運行結束後,才釋放所佔用的內存.
自動型變量存放在堆棧區中.堆棧區也是內存中一部分,該部份內存在程序運行中是重複使用的.