1.1 概要
實(shí)際上“引用計(jì)數(shù)式內(nèi)存管理”的本質(zhì)部分在ARC中并沒(méi)有改變室谚。就像“字段引用計(jì)數(shù)”這個(gè)名詞表示的那樣妹懒,ARC只是自動(dòng)地幫助我們處理“引用計(jì)數(shù)”的相關(guān)部分椅亚。
設(shè)置ARC有效的編譯方法如下所示
- 使用clang(LLVM編譯器)3.0或以上版本
- 指定編譯器屬性為"-fobjc-arc"
Xcode4.2 默認(rèn)設(shè)定為對(duì)所有的文件ARC有效
1.2 內(nèi)存管理的思考方式
引用計(jì)數(shù)式內(nèi)存管理的思考方式就是思考ARC所引起的變化
- 自己生成的對(duì)象顺囊,自己所持有
- 非自己生成的對(duì)象姻乓,自己也能持有
- 自己持有的對(duì)象不再需要時(shí)釋放
- 非自己持有的對(duì)象無(wú)法釋放
1.3 所有權(quán)修飾符
ARC有效時(shí)晰搀,id類(lèi)型和對(duì)象類(lèi)型同C語(yǔ)言器類(lèi)型不同五辽,其類(lèi)型上必須加所有權(quán)修飾符。所有權(quán)修飾符一共有四種厕隧。
- __strong修飾符
- __weak修飾符
- __unsafe_unretained修飾符
- __autoreleasing修飾符
1.3.1 __strong 修飾符
__strong修飾符是id類(lèi)型和對(duì)象類(lèi)型默認(rèn)的所有權(quán)修飾符奔脐。
id obj =[ [NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];
id和對(duì)象類(lèi)型在沒(méi)有明確指定所有權(quán)修飾符時(shí),默認(rèn)為_(kāi)_strong修飾符吁讨,上面兩行代碼相同髓迎。
- 附有__strong修飾符的變量obj在超出其變量作用域時(shí),即在改變量被廢棄時(shí)建丧,會(huì)釋放其被賦予的對(duì)象排龄。
- __strong修飾符的變量,不僅只在變量作用域中翎朱,在賦值上也能夠正確地管理其對(duì)象的所有者橄维。
1.3.2 __weak修飾符
循環(huán)引用容易發(fā)生內(nèi)存泄漏。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對(duì)象在超出其生存周期后繼續(xù)存在 拴曲。
- __weak修飾符與__strong修飾符相反争舞。弱引用不能持有對(duì)象實(shí)例
id __weak obj1 = nil;
{
id __strong obj0 = [[NSObject alloc] init];
obj1 = obj0;
NSLog(@"obj1=%@",obj1);
}
NSLog(@"obj1=%@",obj1);
執(zhí)行結(jié)果:
obj1=<NSObject: 0x604000002010>
obj1=(null)
- __weak修飾符另一個(gè)優(yōu)點(diǎn),在持有對(duì)象的弱引用時(shí)澈灼,若該對(duì)象被廢棄竞川,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài)店溢。
使用__weak修飾符可以避免循環(huán)引用。通過(guò)檢查附有__weak修飾符的變量是否為nil委乌,可以判斷被賦值的對(duì)象是否已經(jīng)被廢棄床牧。
1.3.3 __unsafe_unretained修飾符
__unsafe_unretained修飾符正如其名unsafe所示,是不安全的所有權(quán)修飾符遭贸。盡管ARC式的內(nèi)存管理是編譯器的工作戈咳,但附有__unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象。這一點(diǎn)在使用時(shí)要注意壕吹。
- 附有__unsafe_unretained修飾符的變量同附有__weak修飾符的變量一樣著蛙,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有,所以生成的對(duì)象會(huì)立即被釋放算利。
- 在iOS4以及OS X Snow Leopard的應(yīng)用程序中册踩,必須使用__unsafe_unretained修飾符來(lái)替代__weak修飾符泳姐。賦值給附有__unsafe_unretained修飾符變量的對(duì)象在通過(guò)該變量使用時(shí)效拭,如果沒(méi)有確保其確實(shí)存在,那么應(yīng)用程序會(huì)奔潰
1.3.4 __autoreleasing 修飾符
在ARC有效時(shí)胖秒,用@autoreleasepool塊替代NSAtoreleasePool類(lèi)缎患,用附有__autoreleasing修飾符的變量替代autorelease方法。
為什么在訪問(wèn)附有__weak修飾符的變量時(shí)必須訪問(wèn)注冊(cè)到autoreleasepool的對(duì)象阎肝?因?yàn)開(kāi)_weak修飾符只持有對(duì)象的弱引用挤渔,而在訪問(wèn)對(duì)象的過(guò)程中,該對(duì)象有可能被廢棄风题。如果把要訪問(wèn)的對(duì)象注冊(cè)到autoreleasepool中判导,那么@autoreleasepool塊結(jié)束之前都能確保該對(duì)象存在。因此沛硅,在使用附有__weak修飾符的變量時(shí)必定要使用注冊(cè)到autoreleasepool中的對(duì)象眼刃。
專(zhuān)欄 __strong 修飾符/__weak 修飾符
附有__strong修飾符,__weak修飾符的變量類(lèi)似有C++中智能指針std::shared_ptr和std::weak_ptr。std::shared_ptr可通過(guò)引用計(jì)數(shù)來(lái)持有C++類(lèi)實(shí)例摇肌,std::weak_ptr可避免循環(huán)引用擂红。在不得不使用沒(méi)有__strong 修飾符/__weak 修飾符的C++時(shí),強(qiáng)烈推薦使用這兩種智能指針围小。
2.1 規(guī)則
在ARC有效的情況下編譯源代碼昵骤,必須遵守一定的規(guī)則。
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必須遵守內(nèi)存管理的方法命名規(guī)則
- 不要顯示調(diào)用dealloc
- 使用@autoreleaepool塊替代NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
- 顯示轉(zhuǎn)換“id”和“void”
2.1.1 不能使用retain/release/retainCount/autorelease
內(nèi)存管理是編譯器的工作肯适,因此沒(méi)有必要使用內(nèi)存管理的方法变秦。
設(shè)置ARC有效時(shí),如果編譯器使用了這些方法的源代碼框舔,會(huì)報(bào)錯(cuò)
設(shè)置ARC有效時(shí)蹦玫,禁止再次鍵入retain或者是release代碼
只能在ARC無(wú)效并且手動(dòng)進(jìn)行內(nèi)存管理時(shí)使用retain/release/retainCount/autolease方法
2.1.2 不能使用NSAllocateObject/NSDeallocateObject
在ARC有效時(shí),禁止使用NSAllocateObject函數(shù),如果使用會(huì)引起編譯器錯(cuò)誤
2.1.3 必須遵守內(nèi)存管理的方法命名規(guī)則
在ARC無(wú)效時(shí)钳垮,用于對(duì)象生成/持有的方法必須遵守以下的命名規(guī)則
- alloc
- new
- copy
- mutableCopy
2.1.4 不要顯示調(diào)用dealloc
無(wú)論ARC是否有效惑淳,只要對(duì)象的所有者都不持有該對(duì)象,該對(duì)象就被廢棄饺窿。對(duì)象被廢棄時(shí)歧焦,不管ARC是否有效,都會(huì)調(diào)用對(duì)象的dealloc方法肚医。
2.1.5 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體的成員
C語(yǔ)言的結(jié)構(gòu)體(struct或union)成員中绢馍,如果存在Objective-C對(duì)象型變量,會(huì)引起編譯器錯(cuò)誤肠套。
2.1.6 顯示轉(zhuǎn)換id和void *
在ARC有效時(shí)舰涌,將id變量強(qiáng)制轉(zhuǎn)換void *變量會(huì)引起編譯器錯(cuò)誤
id obj = [[NSObject alloc] init];
void *p = obj;
更進(jìn)一步,將void *變量賦值給id變量你稚,調(diào)用起實(shí)例方法瓷耙,編譯器會(huì)報(bào)錯(cuò)
id o = p;
[o release];
id型或?qū)ο笮妥兞抠x值給void *或者逆向賦值時(shí)都需要進(jìn)行特定的轉(zhuǎn)換。如果只想單純地賦值刁赖,額可以使用"__bridge"轉(zhuǎn)換搁痛。
id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
專(zhuān)欄 Objective-C對(duì)象與Core Foundation對(duì)象
- Core Foundation對(duì)象主要使用在用C語(yǔ)言編寫(xiě)的Core Foundation框架中,并使用引用計(jì)數(shù)的對(duì)象宇弛。在ARC無(wú)效時(shí)鸡典,Core Foundation框架中的retain/release分別是CFRetain/CFRelease。
- Core Foundation對(duì)象與Objective-C對(duì)象的區(qū)別很小枪芒,不同之處只在于是由哪一個(gè)框架所生成的彻况。無(wú)論是由哪種框架生成的對(duì)象,一旦生成后舅踪,便能在不同的框架中使用纽甘。Foundation框架的API生成并持有的對(duì)象可以用Core Foundation框架的API釋放。反過(guò)來(lái)也可以硫朦。
- 因?yàn)镃ore Foundation對(duì)象與Objective-C對(duì)象沒(méi)有區(qū)別贷腕。所以在ARC無(wú)效時(shí),只用簡(jiǎn)單的C語(yǔ)言的轉(zhuǎn)換也能實(shí)現(xiàn)互換咬展。另外這種轉(zhuǎn)換不需要使用額外的CPU資源泽裳。因此被稱(chēng)為“免費(fèi)橋”。
3.1 屬性
當(dāng)ARC有效時(shí)破婆,以下可作為這種屬性聲明中使用的屬性來(lái)用
屬性聲明的屬性 | 所有權(quán)修飾符 |
---|---|
assign | __unsafe_unretained修飾符 |
copy | __strong 修飾符(但是負(fù)責(zé)的是被復(fù)制的對(duì)象) |
retain | __strong修飾符 |
strong | __strong修飾符 |
unsafe_unretained | __unsafe_unretained修飾符 |
weak | __weak修飾符 |
在聲明類(lèi)成員變量時(shí)涮总,如果同屬性聲明中的屬性不一致會(huì)引起編譯錯(cuò)誤。如下例
@property(nonatomic, weak) id obj; // 編譯器報(bào)錯(cuò)
@property(nonatomic, weak) id __weak obj; // 正確祷舀,附加__weak修飾符
@property(nonatomic, strong) id obj; // 正確瀑梗,使用strong
4.1 數(shù)組
如下例代碼烹笔,將nil代入到malloc函數(shù)所分配的數(shù)組各元素中來(lái)初始化是非常危險(xiǎn)的。
arrar = (id __strong *)malloc(sizeof(id) * entries);
for (NSUInteger i = 0; i < entries; i++)
array[i] = nil;
這是因?yàn)橛蒻alloc函數(shù)分配的內(nèi)存區(qū)域中沒(méi)有被初始化為0抛丽,因此nil會(huì)被賦值給附有__strong修飾符的并被賦值了隨機(jī)的變量中谤职,從而釋放一個(gè)不存在的對(duì)象。在分配內(nèi)存時(shí)推薦使用calloc函數(shù)亿鲜。
像這樣允蜈,通過(guò)calloc函數(shù)分配的動(dòng)態(tài)數(shù)組就能完全像靜態(tài)數(shù)組一樣使用。
array[0] = [[NSObject alloc] init];
但是蒿柳,在動(dòng)態(tài)數(shù)組中操作附有__strong修飾符的變量與靜態(tài)數(shù)組有很大差異饶套,需要自己釋放所有的元素。
如以下源代碼所示垒探,在只是簡(jiǎn)單地用free函數(shù)廢棄了數(shù)組用內(nèi)存塊的情況下妓蛮,數(shù)組各元素所賦值的對(duì)象不能再次釋放,從而引起內(nèi)存泄漏圾叼。
free(array);
這是因?yàn)樵陟o態(tài)數(shù)組中蛤克,編譯器能夠根據(jù)變量的作用域自動(dòng)插入釋放賦值對(duì)象的代碼,從而動(dòng)態(tài)數(shù)組中褐奥,編譯器不能確定數(shù)組的生存周期咖耘,所以無(wú)從處理。如以下源代碼所示撬码,一定要將nil賦值給所有元素中,使得元素所賦值對(duì)象的強(qiáng)引用失效版保,從而釋放那些對(duì)象呜笑。在此之后,使用free函數(shù)廢棄內(nèi)存塊彻犁。
for (NSUInteger i = 0; i < entries; i++)
array[i] = nil;
free(array);
同初始化時(shí)的注意事項(xiàng)相反叫胁,即使用memset等函數(shù)將內(nèi)存填充為0也不會(huì)釋放所賦值的對(duì)象。這非常危險(xiǎn)汞幢,只會(huì)引起內(nèi)存泄漏驼鹅。對(duì)于編譯器,必須明確地使用賦值給附有__strong修飾符變量的源代碼森篷,所以請(qǐng)注意输钩,必須將nil賦值給所有數(shù)組元素。
使用memcpy函數(shù)拷貝數(shù)組元素以及realloc函數(shù)重新分配內(nèi)存塊也會(huì)有危險(xiǎn)仲智。由于數(shù)組元素所賦值的對(duì)象有可能被保留在內(nèi)存中或是重復(fù)被廢棄买乃,所以這兩個(gè)函數(shù)也禁止使用。
5.1 ARC的實(shí)現(xiàn)
5.1.1 __strong 修飾符
5.1.2 __weak修飾符
- 若附有__weak修飾符的變量所引用的對(duì)象唄廢棄钓辆,則將nil賦值給該變量
- 使用附有__weak修飾符的變量剪验,即是使用注冊(cè)到autoreleasepool中的對(duì)象肴焊。
5.1.3 __autoreleasing修飾符
將對(duì)象賦值給附有__autoreleasing修飾符的變量等同于ARC無(wú)效時(shí)調(diào)用對(duì)象的autorelease方法。
6.1 引用計(jì)數(shù)
uintptr_t _objc_rootRetainCount(id obj)
_objc_rootRetainCount函數(shù)可獲取指定對(duì)象的引用計(jì)數(shù)值功戚。如下例
id __strong obj = [[NSObject alloc] init];
NSLog(@"retain count = %d",_objc_rootRetainCount(obj));
不使用__autoreleasing修飾符娶眷,僅使用附有__weak聲明的變量也能將引用對(duì)象注冊(cè)到autoreleasepool中。