1 傳統內存管理 ide
Objective-C對象的生命週期能夠分爲:建立、存在、消亡。 函數
1.1 引用計數 ui
相似Java,Objective-C採用引用計算(reference counting)技術來管理對象的生命週期。每一個對象都定義有一個整數(稱引用計數器)與之相關聯,該數用以表示當前有多少個指針指向該對象。 spa
1.1.1 操做方法 3d
當某段代碼須要訪問一個對象時,該代碼就將對象的保留計數值加1;當結束訪問時就減1;若引用計數器減到0時,該對象將被銷燬。引用計數器的值由以下三種操做進行控制: 指針
1) 建立 code
當使用alloc、new方法或者經過copy消息(接收到消息的對象會建立一個自身的副本)建立一個對象時,對象的保留計數器值就被初始化爲1。 對象
2) 增長 blog
要增長對象的引用計數器值,能夠給對象發送一條retain消息,即調用對象的retain方法。 教程
3) 減小
要減小對象的引用計數器值,能夠給對象發送一條release消息,即調用對象的release方法。
當一個對象因其引用計數器值爲0時,將被系統銷燬,從而系統自動給該對象發送一條dealloc消息。因此用戶能夠重載對象的dealloc方法,dealloc方法至關是C++的虛構函數,能夠在該函數中釋放申請的內存空間。
表 11 NSObject類內存管理方法
方法 |
描述 |
- (instancetype)retain |
將引用計數器的值加1,可由用戶調用。 |
- (oneway void)release |
將引用計數器的值減1,可由用戶調用。 |
- (NSUInteger)retainCount |
獲取引用計數器的值,可由用戶調用。 |
- (instancetype)autorelease |
將對象添加到自動釋放池中,可由用戶調用。 |
- (struct _NSZone *)zone |
複製方法。 |
以下所示是RetainTracker對象生命週期的引用計數器值:
1.1.2 對象全部權
對象全部權是指實體的一種職責,當某個實體"擁有一個對象"時,就意味着該實體要負責對其擁有的對象進行清理。實體可能擁有對象的狀況有:
若是一個對象內由指向其餘對象的實例變量,則稱該對象擁有這些對象;
若是一個函數建立了一個對象,則稱該函數擁有這個對象。
1.1.3 訪問方法
將類中的成員指針設置爲指向一個外部對象,需經過retain和release方法來操做引用計數值,以下有3種操做方式:
1) 簡單賦值
簡單賦值方式,以下所示:
2) 修復賦值
這種方式是對前一種方式的修復,即修復了未對原來成員變量的引用計數值進行減小操做,但仍存在問題,以下所示:
3) 正確賦值
這種方式是正確的賦值方式,修復了前兩種錯誤方式。即修復了未對原來成員指針的引用計數值操做,也修復了可能出現同一個指針的問題,以下所示:
1.2 自動釋放池
內存管理是一個棘手的問題,如上述所示的setter方法的各類細微問題。因此Cocoa引入了自動釋放池(autorelease pool)概念,這種方式是經過自動釋放池來管理引用計數值的release操做。有兩種方式建立自動釋放池:
1.2.1 使用方式
若要使用自動釋放池來管理對象的release操做,只要在自動釋放池的生命週期內調用被管理對象的autorelease方法,便可將對象的引用計數值委託自動釋放池實體來管理,當自動釋放池實體結束時,將會調用池中對象的release方法,而且只調用一次。以下所示:
1) 關鍵字方式
@autoreleasepool方式的生命週期是從左花括號"{"開始,直到右花括號"}"結束,即執行到了右花括號時,那麼將調用自動釋放池中對象的release方法,來減小相應的引用計數值。
2) 對象方式
NSAutoreleasePool對象的生命週期是從調用其new方法來建立對象開始,直到調用池對象的release方法後,由系統調用釋放池對象的dealloc方法後結束,即自動釋放池在dealloc"虛構函數"中調用被管理對象的release方法來減小相應引用計數值。
1.2.2 釋放池結構
自動釋放池以棧的形式實現:當建立了一個新的自動釋放池時,該池就被添加到棧頂中。因此若某個對象調用autorelease方法時,該對象將被放入最頂端(棧頂)的自動釋放池中,而且棧頂下的釋放池仍未被銷燬,即被添加到棧頂下面釋放池的對象仍未被釋放。
若須要由自動釋放池來管理大量對象時,用戶能夠手動銷燬釋放池,而後再建立新的池對象,以下所示:
Cocoa也使用相似的方法來管理內存,當使用AppKit時,Cocoa會按期自動地爲用戶建立和銷燬自動釋放池。一般是在程序處理當前事件(如鼠標單擊或鼠標按下)之後執行這些操做。
1.3 Cocoa內存管理規則
Cocoa有許多內存管理約定,它們簡化了retain、release和autorelease的使用方法,這些規則有:
2 自動引用計數
自動引用計數爲Automatic Reference Counting (ARC) ,是Objective-C提供了一種自動內存管理的功能。ARC不須要用戶考慮retain和release操做,而是在編譯期添加代碼(retain和release等方法)來保障對象的生命週期,同時可爲對象自動生成合適的dealloc方法。從而讓用戶專一那些感興趣的代碼。以下是引用計數器手動和自動的差別:
圖 21 引用計數器的手動和自動實現的差別
自動的引用計數和手動引用計數方法是互斥的,即不能同時在應用程序中使用ARC技術和手動操做retain和release。如在圖 22所示,若選擇YES時(默認),則啓動ARC功能;若NO,則關閉ARC功能。
圖 22 ARC功能啓動設置
2.1 強制規則
爲了ARC能工做,強制規定了一些新規則,而且這些規則不能在其它編譯器使用。若是用戶違反了這些規則,那麼將獲得一個compile-time錯誤。這些規則爲:
1) 不能顯示調用dealloc方法,同時不能調用和重載retain、release、retainCount和autorelease方法。但能夠重載dealloc方法,固然在dealloc方法中不能調用引用計數器管理方法,同時在dealloc方法中不能調用 [super dealloc],父類的dealloc方法由編譯器自動添加。
2) 不可以使用NSAllocateObject和NSDeallocateObject,但仍可以使用alloc方法建立對象。
3) 不可以使用NSAutoreleasePool對象,但可使用@autoreleasepool代碼塊。
4) 屬性名次不能以new開頭,好比說@property NSString *newString是不容許的。
5) 屬性不能只有一個read-only而沒有其它內存管理特性。若沒有啓用ARC功能,可使用@property (readonly) NSString *title語句,但若是啓用來ARC功能,就必須指定由誰來管理內存。
6) 結構體(struct)和聯合體(union)不能使用ROP做爲成員,如struct {NSString *str}代碼是不被容許.
ARC只對可保留的對象指針(ROPs)有效。可保留的對象指針主要有以下三種:
全部其它指針類型,好比char*和CF對象都不支持ARC特性,若是使用的指針不支持ARC,那麼必須手動管理這些對象空間。 |
2.2 變量修飾詞
2.2.1 保留環
使用引用計數機制時,常常要注意的一個問題是"保留環"(retain cycle),即呈環狀相互引用的多個對象。這將致使內存泄漏,由於循環中的對象其保留計數不會降爲0。對於循環中的每一個對象來講,至少還有另外一個對象引用着它。
如圖 23所示,A的引用計數爲2,而B的引用計數爲1。當A的擁有者"雲"release A後,A和B的引用計數都爲1,致使二者都沒法被釋放。
圖 23 引用計數保留環
2.2.2 修飾詞
目前Objective-C提供4種修飾詞(qualifier)來修飾變量,具體語義爲:
表 21 Objective-C修飾詞
類型 |
語義 |
__strong |
默認修飾詞,當有一個strong指針指向某對象,那麼該對象將一直保持"活躍"狀態; |
__weak |
其不能讓被引用對象一直保持"活躍"狀態。當某個被引對象沒有__strong指針指向它時,那麼其它對象以__weak類型指向上述對象的指針將被自動置爲nil。 |
__unsafe_unretained |
聲明一個弱應用,可是不會自動nil化,也就是說,若是所指向的內存區域被釋放了,這個指針就是一個野指針了。 |
__autoreleasing |
用來修飾一個函數的參數,這個參數會在函數返回的時候被自動釋放。 |
其使用形式爲:
對於圖 23所示的引用環能夠採用弱引用解決,由於在指向的對象釋放以後,這些弱引用就會被設置爲nil。如圖 24所示,帶有__weak的引用環結構,當"雲"向A發送release消息後,A的引用計數爲0,從而釋放A和B對象內存空間。
圖 24 帶有弱引用的環
2.3 Toll-Free Bridging管理
ARC僅支持可保留對象指針,而沒法自動管理Core Foundation對象的空間,必須由用戶手動調用CFRetain 和 CFRelease方法來管理Core Foundation對象。若是在Objective-C 和 Core Foundation-style 對象之間進行轉換時,爲了讓ARC便於工做,那麼須要告訴編譯器哪一個對象是指針的擁有者。爲此Objective-C使用了橋接轉換(bridged cast)技術。
1) __bridge類型操做符
這種操做符是從ROP類型轉換爲non-ROP類型,但只傳遞指針並不會傳遞它的全部權,即指針的全部權仍在轉換以前的對象上。以下所示:
2) __bridge_retained類型操做符
這種操做符將指針全部權從ROP上轉移到non-ROP上。由於ARC只會注意到non-ROP,因此用戶須要經過non-ROP手動釋放其保留計數器的值。這個轉換類型會給non-ROP對象的保留計數器加1,因此須要手動讓它減1,這與標準的內存管理方式相同。以下所示:
3) __bridge_transfer類型操做符
這種轉換符與上一個相反,它將全部權從non-ROP上轉移到ROP上,從而ARC擁有對象並能確保它會像其它ARC對象同樣獲得釋放。
3 參考文獻
[1] Advanced Memory Management programming guide
[2] Transitioning to ARC Release Notes
[3] Objective-C基礎教程(第2版)
[4] Effective Objective-C 2.0