iOS-ARC

本文的內(nèi)容包括

一、所有權(quán)修飾符
二、ARC的基本規(guī)則
三、ARC的實(shí)現(xiàn)

ARC中仍然是通過引用計(jì)數(shù)來管理內(nèi)存,這個(gè)本質(zhì)沒有變。只是,不需要我們手動(dòng)的寫代碼去管理內(nèi)存了,編譯器自動(dòng)幫助我們管理“引用計(jì)數(shù)”相關(guān)的部分。那么ARC是通過什么方式來幫我們管理內(nèi)存的呢?

ARC是通過編譯期運(yùn)行期兩部分來處理的:

  • 編譯期,編譯器不是通過添加retain/release/autorelease這些方法,而是會(huì)直接調(diào)用更底層的C語言函數(shù)(如objc_retain)。
  • 運(yùn)行期,ARC也包含運(yùn)行期組件。比如,某些類方法返回對(duì)象前,為其執(zhí)行了autorelease操作,而大多數(shù)情況,我們會(huì)對(duì)返回的對(duì)象保留,比如:_myPerson = [Person personWithName:@"Kobe"];
    那么其實(shí)就相當(dāng)于先執(zhí)行了一個(gè)autorelease,然后又retain了一下。ARC可以在運(yùn)行期檢測(cè)到這一多余的操作,也就是autorelease后緊跟retain。那么會(huì)它們兩個(gè)會(huì)被改為調(diào)用objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue。objc_autoreleaseReturnValue會(huì)檢查后邊是否緊接著調(diào)用objc_retainAutoreleasedReturnValue,如果是,就不將返回的對(duì)象注冊(cè)到autoreleasepool中而直接傳遞,省略了autorelasepool注冊(cè),實(shí)現(xiàn)了最優(yōu)化。

一、所有權(quán)修飾符

1. __strong修飾符

如它的名字一樣,__strong表示對(duì)對(duì)象的強(qiáng)引用。持有強(qiáng)引用的變量在超出其作用域的時(shí)候被廢棄,隨著強(qiáng)引用的失效,引用的對(duì)象會(huì)隨之釋放。

知識(shí)點(diǎn):

  1. __strong修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,所以一般不寫__strong。
  2. __strong和__weak和 __autoreleasing修飾符一樣,可以將附有這些修飾符的自動(dòng)變量初始化為nil。

2. __weak(iOS5以上才有,之前用__unsafe_unretained)

如它的名字一樣,__weak修飾符表示對(duì)對(duì)象的弱引用。不持有對(duì)象。

知識(shí)點(diǎn):

  1. 可以避免循環(huán)引用。
  2. 變量指向的對(duì)象被銷毀了,變量也會(huì)自動(dòng)置空為nil。

3. __unsafe_unretained

如它的名字一樣,它是不安全的所有權(quán)修飾符。

知識(shí)點(diǎn):

  1. 和__weak一樣表示弱引用,不持有對(duì)象,但不會(huì)置nil,這也正是不安全的原因。
id __unsafe_unretained obj1 = nil;
    {
        id obj0 = [[NSObject alloc] init];
        obj1 = obj0;
        NSLog(@"A:%@",obj1);
    }
    NSLog(@"B:%@",obj1);

輸出A和B雖然是一樣的地址,但是此時(shí)B處obj1已經(jīng)是野指針了。

4. __autoreleasing

通過給對(duì)象附加__autoreleasing修飾符 來替代調(diào)用autorelease方法,把對(duì)象注冊(cè)到autoreleasepool中。

具體使用情況,在這里

二、規(guī)則

1. 不能使用retain/release/retainCount/autorelease

2. 用@autoreleasepool{}代替NSAutoreleasePool對(duì)象

3. 不要顯式的調(diào)用dealloc

多數(shù)情況下在dealloc中刪除已注冊(cè)的代理或觀察者。不用書寫[super dealloc],因?yàn)锳RC已經(jīng)自動(dòng)處理了。

4. 必須遵守內(nèi)存管理的方法命名規(guī)則

  1. alloc/new/copy/mutableCopy,以上述名稱開始的方法在返回對(duì)象時(shí),必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象。這在ARC下,依然沒有變。
  2. ARC下追加了一條命名規(guī)則:
  • init,以init開頭的方法,必須是實(shí)例方法,并且返回對(duì)象,類型應(yīng)為id或該類的的對(duì)象類型,抑或是超類或子類型。該方法基本上只是對(duì)alloc方法返回的對(duì)象進(jìn)行初始化操作并返回。
    注:initialize方法并不包含在上述命名規(guī)則里。

5. 對(duì)象類型不能作為結(jié)構(gòu)體的成員

因?yàn)锳RC把內(nèi)存管理的工作分配給了編譯器,所以編譯器必須能夠知道并管理對(duì)象的生命周期。例如C語言的自動(dòng)變量(局部變量)可以使用該變量的作用域來管理。但是對(duì)于結(jié)構(gòu)體成員來說,是無法實(shí)現(xiàn)的。

6. 顯式轉(zhuǎn)換id和void*

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;

但是,__bridge安全性與__unsafe_unretained類似,甚至更低。很容易造成野指針導(dǎo)致崩潰。
__bridge還有另外兩種轉(zhuǎn)換,__bridge_retained和__bridge_transfer

  1. Core Foundation 對(duì)象類型不在 ARC 管理范疇內(nèi),需要自己管理.
  2. __bridge只做類型轉(zhuǎn)換,但是不修改對(duì)象(內(nèi)存)管理權(quán),原來是ARC管理的還用ARC,原來MRC管理的繼續(xù)用MRC
  3. __bridge_retained(也可以使用CFBridgingRetain)將Objective-C的對(duì)象轉(zhuǎn)換為Core Foundation的對(duì)象,同時(shí)將對(duì)象(內(nèi)存)的管理權(quán)交給我們,后續(xù)需要使用CFRelease或者相關(guān)方法來釋放對(duì)象;
  4. __bridge_transfer(也可以使用CFBridgingRelease)將Core Foundation的對(duì)象轉(zhuǎn)換為Objective-C的對(duì)象,同時(shí)將對(duì)象(內(nèi)存)的管理權(quán)交給ARC。

5. 不能使用NSAllocateObject/NSDeallocObject

6. 不能使用區(qū)域(NSZone)

三、ARC的實(shí)現(xiàn)

1. __strong實(shí)現(xiàn)

賦值給附有__strong修飾符的變量在實(shí)際的程序中是怎樣運(yùn)行的呢?

{
       id __strong obj = [[NSObject alloc] init];
   }

編譯器在超出作用域時(shí)自動(dòng)插入了release。

{
        id __strong obj = [NSMutableArray array];
    }

執(zhí)行,alloc/new/copy/mutableCopy之外的方法,如array類方法:
像代碼這樣,返回注冊(cè)到autoreleasepool中對(duì)象的方法使用了objc_autoreleaseReturnValue,如果其后緊接著調(diào)用objc_retainAutoreleasedReturnValue(),那么就不將返回的對(duì)象注冊(cè)的autoreleasepool中而直接傳遞。通過這兩個(gè)方法,優(yōu)化了程序運(yùn)行。

2.__weak的實(shí)現(xiàn)

id __strong obj = [[NSObject alloc]init];
id __weak obj1 = obj;

objc_initWeak函數(shù)中的weak_register_no_lock()把賦值對(duì)象obj的地址作為鍵值,通過哈希查找找到weak弱引用表中對(duì)應(yīng)的數(shù)組,將附有__weak修飾符變量的指針添加到數(shù)組中。如果沒有找到數(shù)組,表示是第一個(gè)weak指針,則新建一個(gè)數(shù)組。

對(duì)象在被廢棄時(shí)dealloc方法中會(huì)調(diào)用object_dispose函數(shù),該方法內(nèi)部會(huì)通過調(diào)用weak_clear_no_lock()

  1. 通過哈希查找從weak表中獲取廢棄對(duì)象地址作為鍵值的記錄是一個(gè)數(shù)組。
  2. 將包含在數(shù)組中的所有附有__weak修飾符的變量地址,遍歷數(shù)組賦值為nil。
  3. 從weak表中刪除該記錄。
  4. 從weak表中刪除該鍵值。

所以,如果大量使用__weak修飾符的變量,則會(huì)消耗相應(yīng)的cpu資源。良策是只在需要避免循環(huán)引用的時(shí)候使用__weak。

3. __autoreleasing修飾符的實(shí)現(xiàn)

objc_autorelease

4. 引用計(jì)數(shù)

可以通過_objc_rootRetainCount(obj)來獲取對(duì)象的引用計(jì)數(shù)值。但實(shí)際上并不能完全信任該函數(shù)取得的值。對(duì)于已經(jīng)釋放的對(duì)象以及不正確的對(duì)象地址,有時(shí)也返回1。另外,在多線程中使用它,因?yàn)榇嬖诟?jìng)態(tài)條件的問題,所以取得的數(shù)值也不一定完全可信。當(dāng)然它在調(diào)試中還是比較有用的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容