本文的內(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_autoreleaseReturnValue和objc_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):
- __strong修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,所以一般不寫__strong。
- __strong和__weak和 __autoreleasing修飾符一樣,可以將附有這些修飾符的自動(dòng)變量初始化為nil。
2. __weak(iOS5以上才有,之前用__unsafe_unretained)
如它的名字一樣,__weak修飾符表示對(duì)對(duì)象的弱引用。不持有對(duì)象。
知識(shí)點(diǎn):
- 可以避免循環(huán)引用。
- 變量指向的對(duì)象被銷毀了,變量也會(huì)自動(dòng)置空為nil。
3. __unsafe_unretained
如它的名字一樣,它是不安全的所有權(quán)修飾符。
知識(shí)點(diǎn):
- 和__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ī)則
- alloc/new/copy/mutableCopy,以上述名稱開始的方法在返回對(duì)象時(shí),必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象。這在ARC下,依然沒有變。
- 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
- Core Foundation 對(duì)象類型不在 ARC 管理范疇內(nèi),需要自己管理.
- __bridge只做類型轉(zhuǎn)換,但是不修改對(duì)象(內(nèi)存)管理權(quán),原來是ARC管理的還用ARC,原來MRC管理的繼續(xù)用MRC
- __bridge_retained(也可以使用CFBridgingRetain)將Objective-C的對(duì)象轉(zhuǎn)換為Core Foundation的對(duì)象,同時(shí)將對(duì)象(內(nèi)存)的管理權(quán)交給我們,后續(xù)需要使用CFRelease或者相關(guān)方法來釋放對(duì)象;
- __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()
- 通過哈希查找從weak表中獲取廢棄對(duì)象地址作為鍵值的記錄是一個(gè)數(shù)組。
- 將包含在數(shù)組中的所有附有__weak修飾符的變量地址,遍歷數(shù)組賦值為nil。
- 從weak表中刪除該記錄。
- 從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)試中還是比較有用的。