iOS 內(nèi)存管理
iOS 的引用計(jì)數(shù)就類似于下圖中進(jìn)出辦公室的開關(guān)燈流程。當(dāng)一個人A進(jìn)入前,辦公室的引用數(shù)為0,進(jìn)入后需要照明,因此開燈,引用數(shù)為1,B進(jìn)入后,引用數(shù)為2,C 進(jìn)入后引用數(shù)為3,以此類推。A 離開時,引用數(shù)-1,當(dāng)最后一個人離開辦公室時,引用數(shù)為0,不再需要照明,因此關(guān)燈。

對應(yīng)到 OC 對象的動作時,開燈=生成對象(allocnewcopymutableCopy),需要照明=持有對象(retain),不需要照明=釋放對象(release),關(guān)燈=廢棄對象(dealloc)。

注意這些原則里的一些關(guān)鍵詞與方法的對應(yīng)關(guān)系:『生成』- allocnewcopymutableCopy『持有』- retain『釋放』- release『廢棄』- dealloc
下面分別來解釋一下,這四條原則的含義:
1.1.1 自己生成的對象,自己持有id obj = [[NSObject alloc] init]; id obj = [NSObject new]; id obj = [NSObject copy]; id obj = [NSObject mutableCopy]; // 注意 allocnewcopymutableCopy 開頭的駝峰式方法名,也生成并持有對象 id obj = [MyObject allocMyObj]; id obj = [MyObject newThatObj]; id obj = [MyObject copyThis]; id obj = [MyObject mutableCopyThat];
注意allocnewcopymutableCopy 開頭非駝峰式命名的方法不適用上述規(guī)則。例如:allocatenewercopyingmutableCopyed。
1.1.2 非自己生成的對象,也可持有id obj = [NSMutableArray array];
obj是非『allocnewcopymutableCopy』或以其開頭的駝峰式命名方法創(chuàng)建,因此屬于非自己生成的對象。如何持有對象呢?用 retain 啊~
id obj = [NSMutableArray array];[obj retain];1.1.3 釋放不再需要自己持有的對象
用allocnewcopymutableCopy』或以其開頭的駝峰式命名方法生成并持有的對象,在不再需要的時候,要用 release 方法釋放。
id obj = [[NSMutableArray alloc] init]; // do Something... [obj release];
id obj = [NSMutableArray array];[obj retain]; // do Something... [obj release];1.1.4 非自己持有的對象無法釋放
釋放非自己持有的對象時,會發(fā)生崩潰,例如
1)同一個對象被多次釋放:
id obj = [[NSMutableArray alloc] init];[obj release];[obj release];
2)釋放非自己持有的對象:
id obj = [obj0 object]; [obj release]; // obj 既不是 allocnewcopymutableCopy 出來的,也沒有 retain,因此沒有被持有,不可以被釋放2. autorelease
有一個跟 release 類似的關(guān)鍵詞autorelease,看這樣一段代碼:
- (id)object { id obj = [[NSMutableArray alloc] init]; [obj autorelease]; return obj;}
obj 對象在什么時候被釋放呢?與 release 的區(qū)別是什么?

對象被 release 時,引用計(jì)數(shù)-1,當(dāng)引用計(jì)數(shù)為0時,該對象被立即釋放。而對象被 autorelease 時,引用計(jì)數(shù)不變,該對象被注冊到自動釋放池中,在一個運(yùn)行周期結(jié)束時,自動釋放池被傾倒(池中注冊的對象被 release)。
autorelease 類似 C 語言中的局部變量的特性,局部變量超過其作用域時會被自動廢棄,autorelease 對待對象實(shí)例與之類似。當(dāng)超出 autorelease 的作用域時,對象實(shí)例的 release 方法被調(diào)用。與 C 語言局部變量不同的是,autorelease 可以設(shè)置其作用域。
for(int i = 0;i < 10000; i ++){ @autoreleasepool { // 在一個 runloop 周期內(nèi)產(chǎn)生大量對象的代碼 }}
除了上述場景,總結(jié)一下需要顯式調(diào)用 autoreleasepool 的情況:
顯式使用@autoreleasepool:
autorelease 機(jī)制基于 UI framework,因此寫 非UI framework的程序時,需要自己管理對象生存周期。
autorelease 觸發(fā)時機(jī)發(fā)生在下一次runloop的時候。因此如何在一個大的循環(huán)里不斷創(chuàng)建autorelease對象,那么這些對象在下一次runloop回來之前將沒有機(jī)會被釋放,可能會耗盡內(nèi)存。這種情況下,可以在循環(huán)內(nèi)部顯式使用@autoreleasepool {}將autorelease 對象釋放。
for (item in BigSet){ @autoreleasepool { //create large mem objects }}
自己創(chuàng)建的線程。Cocoa的應(yīng)用都會維護(hù)自己autoreleasepool。因此,代碼里spawn的線程,需要顯式添加autoreleasepool。
很長的函數(shù)、很多中間變量時。正常情況下,你創(chuàng)建的變量會在超出其作用域的時候被釋放掉。而如果函數(shù)寫的很長,在函數(shù)運(yùn)行過程中出現(xiàn)很多中間變量,占據(jù)了大量的內(nèi)存,怎么辦?用@autoreleasepool。在@autoreleasepool中創(chuàng)建的變量,會在@autoreleasepool結(jié)束的時候執(zhí)行一次release,進(jìn)行釋放。其實(shí)@autoreleasepool就相當(dāng)于一層作用域。
3. ARC的規(guī)則ARC 是從 iOS5出現(xiàn)的編譯器新特性,對引用采取自動計(jì)數(shù),不再需要手動的對對象進(jìn)行 retain 和 release,編譯器代替我們來做這件事了。
可以通過設(shè)置配置文件,在同一個項(xiàng)目中既有 ARC 也有 MRC(例如受老項(xiàng)目或老第三方庫影響,需要在 ARC 項(xiàng)目中加入 MRC 的類)。
在 ARC 項(xiàng)目中用到 MRC:在targets的build phases選項(xiàng)下Compile Sources下選擇要不使用arc編譯的文件,雙擊,輸入 -fno-objc-arc ; 在 MRC 中用到 ARC:同上步驟,選擇要使用arc編譯的文件,雙擊,輸入 -fobjc-arc ;3.1 所有權(quán)修飾符ARC 同 MRC 一樣,仍使用引用計(jì)數(shù),仍適用1.1中內(nèi)存管理的4條原則。ARC為何能自動釋放呢?關(guān)鍵因素就是—— ARC 中增加了4種所有權(quán)修飾符:
__strong__weak__unsafe_unretained__autoreleasing其中,__strong__weak__autoreleasing對修飾的局部變量初始化為 nil。
以下著重介紹常用的__strong__weak修飾符.
3.1.1 __strong__strong 是 id 類型、對象類型的默認(rèn)所有權(quán)修飾符,在 ARC 有效時不需要顯式寫出,如以下兩行代碼在 ARC 下是相同的:
id obj = [[NSObject alloc] init]; id __strong obj = [[NSObject alloc] init];
__strong 修飾符表示對對象強(qiáng)引用,在超過作用域時廢棄,釋放所引用的對象及其成員。相當(dāng)于 MRC 中對該對象調(diào)用 release 方法。
__strong在 ARC中是如何實(shí)現(xiàn) MRC的功能的?對比 MRC,MRC 通過手動寫[obj release]來釋放自己創(chuàng)建并持有的內(nèi)存;
ARC 通過增加所有權(quán)修飾符這個概念,對 id|對象類型自己創(chuàng)建且持有的對象默認(rèn)添加__strong 修飾符,從手動寫 release 語句變?yōu)橥ㄟ^作用域控制對象及其成員的釋放。
ARC 對非自己創(chuàng)建但持有的對象,也通過默認(rèn)添加 __strong修飾符強(qiáng)引用,使其持有(相當(dāng)于 MRC 中 retain 語句)對象。
回顧 MRC 內(nèi)存管理的4條原則:
自己生成的對象,自己持有也可持有非自己生成的對象釋放不再需要自己持有的對象非自己持有的對象無法釋放『自己生成的對象,自己持有』『也可持有非自己生成的對象』,ARC 中對 id對象類型默認(rèn)添加__strong 修飾符進(jìn)行強(qiáng)引用;『釋放不再需要自己持有的對象』變量作用域結(jié)束成員所屬對象廢棄對變量賦值都可以滿足這條;『非自己持有的對象無法釋放』ARC 中不再需要寫 release 語句,因此這條也滿足。因此 ARC 也是完全遵守 MRC 內(nèi)存管理的原則的。
3.1.2 __weak為解決__strong 導(dǎo)致的循環(huán)引用問題,進(jìn)而造成內(nèi)存泄露(廢棄的對象在超出其生存周期后繼續(xù)存在),需要引入 _weak 修飾符,對造成循環(huán)引用的對象進(jìn)行弱引用。當(dāng)作用域結(jié)束時,被強(qiáng)引用的對象廢棄,弱引用的對象自動被置為 nil。
3.1.3 __unsafe_unretainediOS4之前用,類似__weak(iOS 5),但需要在使用被其修飾的變量時,先判斷是否存在。
3.1.4 __autoreleaseautorelease 修飾符同strong 一樣一般不顯式寫出。在 MRC中,通過 NSAutoreleasePool 對象的聲明和 drain 代碼之間調(diào)用 autorelease 進(jìn)行自動釋放。在 ARC中,把需要自動釋放的代碼寫在@autorelease{// code ...}中,當(dāng)區(qū)間內(nèi)非 allocnewcopymutableCopyinit 的對象會被自動加入 autoreleasepool,在作用域結(jié)束的時候釋放。如下圖:

來源:簡書
相關(guān)文章:
