23. 通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對象間通信
我們實際編碼時已經(jīng)經(jīng)常使用到protocol的技術(shù)了(委托代理模式)
定義代理屬性時,切記使用weak而非strong,避免“保留環(huán)”
@property (nonatomic, weak) id<EOCSomeDelegate> delegate;
24. 將類的實現(xiàn)代碼分散到便于管理的幾個分類中
為了避免一個實現(xiàn)文件太大内斯,實現(xiàn)的方法太多蹦浦,可以根據(jù)功能將類的實現(xiàn)分到不同的分類中篇裁。
EOCPerson類可以分成幾個不同的實現(xiàn)文件:
EOCPerson+Friendship(.h/.m)
EOCPerson+Work(.h/.m)
EOCPerson+Play(.h/.m)
如果要使用分類中的方法,記得引入分類的頭文件肾砂。
這樣分散到分類中的好處是:
- 便于調(diào)試,編譯后的符號表中宏悦,分類中的方法符號會出現(xiàn)分類的名稱镐确。
- 如果將私有方法放在名為Private的分類中,那很容易看到調(diào)試錯誤原因饼煞,并且在編寫通用庫供他人使用時源葫,私有分類的頭文件不公開,只能程序庫自己能用砖瞧。
25. 為第三方類的分類名稱加前綴
分類機(jī)制常用在向無源碼的類中新增功能息堂。
將分類方法加入源類的操作是在運行期間系統(tǒng)加載分類時完成的。
如果分類中的方法名稱與類中已有的方法名一樣块促,分類中的方法就會覆蓋原來的實現(xiàn)荣堰。解決辦法就是給方法加前綴。
在整個應(yīng)用程序中竭翠,類的每個實例都可以調(diào)用分類的方法振坚。
26. 勿在分類中聲明屬性
除了"class-cotinuation分類",其他分類都無法向類中新增實例變量斋扰,它們無法把實現(xiàn)屬性所需的實例變量合成渡八。
當(dāng)然啃洋,使用關(guān)聯(lián)對象可以解決這種無法合成實例變量的問題:
#import <objc/runtime.h>
static const char *kFriendsPropertyKey = "kFriendsPropertyKey";
@implementation EOCPerson (Friendship)
- (NSArray *)friends {
return objc_getAssociatedObject(self,kFriendsPropertyKey);
}
- (void)setFriends:(NSArray *)friends {
objc_setAssociatedObject(self,kFriendsPropertyKey,
friends,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
但是這樣不理想,因為內(nèi)存管理語義容易出錯呀狼。萬一你修改了屬性的內(nèi)存管理語義裂允,還要記得在設(shè)置方法中修改關(guān)聯(lián)對象所用的內(nèi)存管理語義。所以不推薦這樣做哥艇。
屬性應(yīng)該都定義在主接口里绝编。分類的目的在于擴(kuò)展功能,而非封裝數(shù)據(jù)貌踏。
27. 使用"class-continuation分類"隱藏實現(xiàn)細(xì)節(jié)
"class-continuation分類"就是我們常寫在實現(xiàn)文件中的這樣一段代碼:
@interface EOCPerson ()
//property here
@end
這樣十饥,可以將方法或者實例變量隱藏在本類中使用,而不暴露給公共接口祖乳。
如果屬性在主接口中聲明為只讀逗堵,而類內(nèi)部又要修改屬性值,就可以在class-continuation分類中將其擴(kuò)展為可讀寫眷昆。
28. 通過協(xié)議提供匿名對象
協(xié)議可以在某種程度上提供匿名類型蜒秤。具體的對象類型可以淡化成遵守某協(xié)議的id類型。
使用匿名對象來隱藏類型名稱亚斋。
如果具體類型不重要作媚,重要的是對象能夠響應(yīng)特定方法,那么可使用匿名對象來表示帅刊。
29. 引用計數(shù)
引用計數(shù)變?yōu)?后“可能”就釋放內(nèi)存了纸泡,其實只是放回“可用內(nèi)存池”,如果沒有被覆寫之前仍然可以訪問赖瞒,但這是很危險的女揭,因為這樣很可能出現(xiàn)野指針,造成程序崩潰栏饮。
弱引用避免保留環(huán)吧兔。
30. 以ARC簡化引用計數(shù)
ARC是會自動執(zhí)行retain、release袍嬉、autorelease的掩驱,所以在ARC模式下是不可以直接調(diào)用內(nèi)存管理方法的,具體如下方法:
- retain
- release
- autorelease
- dealloc
實際上冬竟,ARC不是通過OC的消息派發(fā)機(jī)制的欧穴,而是直接調(diào)用底層的C語言版本比如objc_retain”门梗可以節(jié)省很多CPU周期涮帘。
若方法名以下列詞語開頭,則返回的對象歸調(diào)用者所有, 即調(diào)用的代碼要負(fù)責(zé)釋放對象笑诅。
- alloc
- new
- copy
- mutableCopy
若方法名不以上述四個詞語開頭调缨,則表示返回的對象不歸調(diào)用者所有疮鲫,返回的對象會自動釋放。
這些規(guī)則所需要的內(nèi)存管理事宜都有ARC自動處理弦叶。
ARC在運行期還可以起到優(yōu)化作用俊犯, 比如在autorelease之后立馬又調(diào)用retain的場景下,ARC在運行期可以檢測這種多余的操作伤哺,利用全局?jǐn)?shù)據(jù)結(jié)構(gòu)中的一個標(biāo)志位來決定是否需要真正執(zhí)行autorelease和retain操作燕侠。
ARC只負(fù)責(zé)管理OC對象的內(nèi)存,而CoreFoundation對象不歸ARC管理立莉,還需要開發(fā)者適當(dāng)調(diào)用CFRetain/CFRelease绢彤。
31. 在dealloc方法中只釋放引用并解除監(jiān)聽
每個對象生命周期結(jié)束后最終為系統(tǒng)回收,執(zhí)行一次且僅一次dealloc方法蜓耻。
在此方法中釋放對象所擁有的所有引用茫舶,ARC會自動生成.cxx_destruct方法。
此方法還要做一件重要的事情刹淌,就是把配置的observation behavior都清理掉饶氏,比如NSNotificationCenter給此對象訂閱過某種通知,那么應(yīng)該在此注銷有勾。否則繼續(xù)給對象發(fā)送通知的話會導(dǎo)致crash疹启。
- (void)dealloc {
CFRelease(coreFoundationObject);
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
如果非ARC模式,最后還要調(diào)用[super dealloc]柠衅,ARC模式下就不需要。
對于開銷較大或者系統(tǒng)稀缺的資源(如文件描述符籍琳、套接字菲宴、大塊內(nèi)存等),應(yīng)該使用"清理方法"而非dealloc來釋放趋急。比如網(wǎng)絡(luò)連接使用完畢后喝峦,調(diào)用close方法。
這樣做的原因是:
- 避免保留稀缺資源的時間過長呜达。
- 系統(tǒng)為了優(yōu)化程序效率谣蠢,不保證每個對象的dealloc方法都被執(zhí)行。
在dealloc中查近,可以檢測資源是否執(zhí)行了清理操作眉踱,沒有的話可以輸出錯誤信息并執(zhí)行一次清理操作。
有些方法不應(yīng)該在dealloc方法里調(diào)用霜威,比如
- 執(zhí)行異步任務(wù)的方法
- 需要切換到特定線程執(zhí)行的方法
- 屬性的存取方法
32. 編寫“異常安全代碼”時留意內(nèi)存管理問題
雖然OC中異常只應(yīng)發(fā)生在嚴(yán)重的錯誤中谈喳,但是有時候還是要編寫代碼來捕獲異常。
在捕獲異常時要管理好內(nèi)存戈泼,防止泄漏婿禽。
比較下面的兩種處理方法赏僧,明顯后者更合適:
(1)
@try {
EOCSomeClass *object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
[object release];//此處可能執(zhí)行不到,造成內(nèi)存泄漏
}
@catch (...) {
NSLog(@"exception");
}
(2)
EOCSomeClass *object;
@try {
object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
}
@catch (...) {
NSLog(@"exception");
}
@finally {
[object release];//此處總會執(zhí)行到扭倾。
}
以上是非ARC模式下的做法淀零,如果是ARC模式也這樣try/catch就有很大問題了,因為ARC針對這種情況不會自動處理release膛壹,這樣做的代價很大驾中。但是ARC還是可以生成安全處理異常所用的代碼,只需要打開-fobjc-arc-exceptions編譯器標(biāo)志恢筝。
所以哀卫,總的來說,如果非ARC模式下必須捕獲異常撬槽,那就設(shè)法保證代碼能把對象清理干凈此改; 如果是ARC下必須捕獲異常,就要打開-fobjc-arc-exceptions標(biāo)志侄柔。當(dāng)然共啃,如果發(fā)現(xiàn)程序有大量異常捕獲操作時,說明你的代碼需要重構(gòu)了暂题。