29. 理解引用計數
保留計數:表示當前有多少個事物想令此對象繼續存活下去。也可以叫“引用計數”(reference count)。NSObject協議聲明了3個方法用于操作計數器:
- retain 遞增保留計數
- release 遞減保留計數
- autorelease 帶稍后清理“自動釋放池”時,再遞減保留計數。
根對象:兩者都是應用程序啟動時創建的單例。
Mac OS:此對象是NSApplication對象
iOS:此對象是UIApplication對象
要點總結
- 引用計數機制通過可遞增遞減的計數器來管理內存。對象創建好之后,引用計數最少為1。若保留計數為正,則繼續保留。當保留計數為0時,對象就被銷毀了。
- 在對象的聲明周期中,其它對象可通過引用計數保留或釋放對象。保留和釋放操作分別會遞增、遞減引用計數。
30. 以 ARC 簡化引用計數
要點總結
啟用 ARC 之后,程序員就無需擔心內存管理問題了。使用 ARC 來編碼,可以少寫很多樣板式代碼。
ARC 管理對象生命周期的方法就是:在合適的地方自動插入“保留”或“釋放”操作。在 ARC 環境下,變量的內存管理語義可以通過修飾符知名,而原來則需要手動進行“保留”及“釋放”操作。
由方法返回的對象,其內存管理語義總是通過方法名來實現。ARC 將此確定為開發者必須遵守的規則。
ARC 只負責管理Objective-C對象的內存。尤其要注意:CoreFoundation 對象不歸 ARC 管理,開發者必須適時調用CFRetain/CFRelease。
31. 在 dealloc 方法中只釋放引用并解除監聽
使用 ARC 時必須遵循的方法名規則
若方法名以下列詞語開頭,則其返回的對象歸調用者所有:
- alloc
- new
- copy
- mutableCopy
注意:此時的對象的保留計數為正值。
變量的內存管理語義
- __strong: 默認語義,保留其值
- __unsafe_unretained: 不保留此值。這么做可能不安全,因為再次使用該變量時,其對象可能已經被回收了。
- __weak: 不保留此值,但是變量可以安全使用,因為如果系統把這個變量回收了,那么變量也會自動清空。
- __autoreleasing: 把對象“按引用傳遞”給方法時,使用這個特殊的修飾符。此值在方法返回時自動釋放。
要點總結
- 在 dealloc 里面,要做的就是釋放指向其它對象的引用,并取消“鍵值監察”(KVO) 或 NSNotificationCenter 等通知,不要做其他事情。
- 如果對象持有“系統文件描述符”等系統資源,那么應該專門編寫一個方法以釋放這種資源。這樣的類要和其使用者約定,用完資源后必須調用close方法。
- 執行異步任務的方法不應在dealloc里調用;只有在正常狀態下執行的那些方法也不應在 dealloc 里調用,因為此時對象已經處于正在回收的狀態了。
32. 編寫 “異常安全代碼” 時留意內存管理問題
要點總結
- 捕獲異常時,一定要注意將 try 塊內所創立的對象清理干凈。
- 在默認情況下,ARC 不生成安全處理異常所需的清理代碼。開啟編譯器標志后,可以生成這種代碼,不過會導致應用程序變大,而且會降低運行效率。
33. 以弱引用避免保留環
要點總結
- 將某些引用設置為weak,避免出現“保留環”。
- weak 引用可以自動清空,也可以不自動清空。自動清空是隨著 ARC 而引入的新特性,由運行期系統來實現。在具備自動清空功能的若引用上,可以隨意讀取其數據,因為這種引用不會指向已經回收過的對象。
34. 以“自動釋放池塊”降低內存峰值
要點總結
- 自動釋放池排布在棧中,對象收到 autorelease 消息后,系統將其放入最頂端的池里。
- 合理運用自動釋放池,可以降低應用程序的內存峰值。
- @autorelease 這種寫法能夠創建出更為輕便的自動釋放池。
35. 用 “僵尸對象” 調試內存管理問題
要點總結
- 系統在回收對象時,可以不將其真的回收,而是將其轉成僵尸對象。通過環境變量 NSZombieEnabled 可開啟此功能。
- 開啟后,系統會修改對象 isa 指針,使其指向特殊的僵尸類,從而使該對象變為僵尸對象。僵尸類能夠響應所有的選擇子,響應方式為:打印一條包含消息內容及其接收者的消息,然后終止程序。
36. 不要使用 retainCount
要點總結
- 對象的保留計數看似有用,實則不然,因為任何給定時間點上的“絕對保留引用計數”都無法反映對象生命周期的全貌。
- 引用 ARC 之后,retainCount方法就正式廢止了,在 ARC 模式下調用該方法會導致編譯器報錯。