第一條 瞭解Objective-C預言起源
起源:Smalltalk
類型:使用消息結構的語言
區別:使用消息結構的語言,其運行時所應執行的代碼由運行環境來決定;而使用函數調用的語言則由編譯器決定。
對象分配在堆空間上,指針分配在棧空間上
結構體分配在棧空間(CGRect)
第二條 在類的頭文件中儘量少引入其他頭文件
用OC編寫任何類幾乎都需要引入Foundation.h。
向前聲明:@class EOCEmployer;
使用#import而非#include互相引用時不會死循環
要點:
1、除非有必要,不要引入頭文件。應在類的頭文件中使用向前聲明來提及別的類,在實現文件中引入那個類的頭文件。可以儘量降低類之間的耦合。
2、無法使用向前聲明,如聲明某個類遵循一項協議。儘量把“遵循某協議”的聲明移到“class-continuation分類”中。若不行,就把協議單獨放在一個頭文件中,再引入。
第三條 多用字面量語法,少用與之等價的方法
字面量語法只是一種“語法糖”
疑問:mrc下字面量創建的對象到底有沒有reatain+1
使用字面量語法創建出來的字符串、數組、字典對象都是不可變的,若想要可變版本的對象,需要複製一份
要點:
1、使用字面量語法來創建字符串、數值、數組、字典。
2、通過取下標操作來訪問數組下標或字典中的鍵所對應的元素
3、用字面量語法創建數組或字典時,若值中有nil,則會拋出異常。因此,務必確保值裡不含nil
第四條 多用類型常量,少用#define 預處理指令
#define ANIMATION_DURATION 0.3
static const NSTimeInterval kAnimationDuration = 0.3;
若不打算公開某個常量,則應將其定義在使用該常量的實現文件裡。static修飾符意味著該變量僅在定義此變量的編譯單元可見。在OC語境下,“編譯單元”指每個類的實現文件(以.m為後綴名)。假如聲明此變量時不加static,則編譯器會為它創建一個“外部符號”。此時若是另一個編譯單元中也聲明瞭同名變量,那麼編譯器就會拋出錯誤。
如果一個變量既聲明為static,又聲明為const,編譯器根本不會創建符號,而是會像#define一樣,把所遇到的變量都替換成常值。
常量放在“全局符號表中”:
//In the header file
extern NSString *const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant = @"VALUE";
第五條 用枚舉表示狀態、選項、狀態嗎
要點:
如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那麼就將各選項值定義為2的冪,以便通過按位或操作將其組合起來。
用NS_ENUM與NS_OPTIONS宏定義枚舉類型,並指明底層數據類型。這樣可以確保枚舉是用開發者所選的底層數據類型實現出來,而不會採用編譯器所選的類型。
在處理枚舉類型的switch語句中不要實現default分支。這樣的話,加入新枚舉之後,編譯器就會提示開發者:switch語句並未處理所有枚舉。
第六條 理解 “屬性” 這一概念
編譯器會把“點語法”轉換為對存取方法的調用,使用“點語法”的效果與直接調用存取方法相同。
如果使用了屬性,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫“自動合成(autosynthesis)”。這個過程由編譯器在編譯期執行,所以編輯器裡看不到“合成方法(synthesied method)”的源代碼。除了生成方法代碼,編譯器還要自動向類中添加適當類型的實例變量,並且在屬性名前面加下劃線,以此作為實例變量的名字。也可以在類的實現代碼裡通過@synthesize語法來指定實例變量的名字
@implementation EOCPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
有一種辦法能阻止編譯器自動合成存取方法,就是使用@dynamic關鍵字,它會告訴編譯器:不要自動創建實現屬性所用的實例變量,也不要為其創建存取方法。而且,在編譯訪問屬性的代碼時,即使編譯器發現沒有定義存取方法,也不會報錯,它相信這些方法能在運行期找到。
@interface EOCPerson : NSManagedObject
@property NSString *firstName;
@property NSString *lastName;
@end
@implementation EOCPerson
@dynamic firstName, lastName;
@end
屬性的特質:原子性,讀/寫權限,內存管理語義,方法名
要點:
用@property語法來定義對象中所封裝的數據
通過“特質”來指定存儲數據所需的正確語義
在設置屬性所對應的實例變量時,一定要遵從該屬性所聲明的語義
開發iOS程序時應該使用nonatomic屬性,因為atomic屬性會嚴重影響性能
第七條 在對象內部儘量直接訪問實例變量
直接訪問實例變量,不會觸發 kvo通知。
在寫入實例變量時,通過設置方法來做,在讀取實例變量時,則直接訪問。
在初始化方法中設置屬性值應該直接訪問實例變量,因為子類可能會覆寫設置方法
delloc中直接通過
惰性化初始技術需要採用存取方法。
第八條 理解“對象等同性” 這一概念
使用NSObject協議中聲明的 isEqual 方法來判斷兩個對象的等同性
NSObject協議中兩個用於判斷等同性的關鍵方法:
- (BOOL)isEqual:(id)object;
- (NSUInterger)hash;
等同性約定
如果 isEqual: 判定兩個對象相等,那麼hash方法也必須返回同一個值.但是,如果兩個對象的hash方法返回同一個值, isEqual: 未必會認為兩者相等
有一種情況要注意,在容器中放入可變類對象時,把某個對象放入collection之後,就不應再改變其哈希碼了。
要點:
若想檢測對象的等同性,請提供“isEquel:”與hash方法
相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象卻未必相同
第九條 以“類族模式”隱藏實現細節
類族可以隱藏“抽象基類”背後的實現細節。
系統框架有很多類族。大部分collection類都是類族。
在傳統的類族模式中,通常只有一個類具備“公共藉口”,這個類就是類族中的抽象基類
Cocoa中NSArray這樣的類族來說,新增子類需遵守幾條規則:
自類應該繼承自類族中的抽象基類
子類應該定義自己的數據存儲方式
子類應當覆寫超類文檔中指明需要覆寫的方法
第十條 在既有類中使用關聯對象存放自定義數據
設置關聯對象
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
取關聯對象值
id objc_getAssociatedObject(id object, void *key)
移除指定對象的全部關聯對象
void objc_removeAssociatedObjects(id object)
要點
定義關聯對象可指定內存管理語義,用以模仿定義屬性時所採用的“擁有關係”與“非擁有關係”
只有在其他做法不可行時才應選用關聯對象,因為這種做法通常會引入難於查找的bug
閱讀更多 大大眾小小粽 的文章