ARC在編譯期間,根據(jù)Objective-C對(duì)象的存活周期,在適當(dāng)?shù)奈恢锰砑觬etain和release代碼。從概念上講,ARC與手動(dòng)引用計(jì)數(shù)內(nèi)存管理遵循同樣的內(nèi)存管理規(guī)則,但是ARC也無法防止循環(huán)強(qiáng)引用。
ARC模式下的修飾符來修飾變量和聲明屬性:
- 聲明變量的修飾符:__strong, __weak, __unsafe_unretained, __autoreleasing
- 聲明屬性的修飾符:strong, weak, unsafe_unretained,assign
- 對(duì)象和CoreFoundation對(duì)象直接的轉(zhuǎn)換修飾符號(hào):__bridge,__bridge_retained或CFBridgingRetain, __bridge_transfer或CFBridgingRelease。
- 對(duì)于線程的安全,有nonatomic,這樣效率就更高了,但是不是線程的。如果要線程安全,可以使用atomic,這樣在訪問是就會(huì)有線程鎖。
這里主講iOS ARC所以就只講weak strong assign這三個(gè)修飾符,__weak __strong __assign跟前面的一樣
__strong 修飾符
先來看一段代碼:
NSObject *obj1 = [[NSObject alloc]init];
id obj2 = obj1;
__strong 修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,也就是說上面的變量,實(shí)際上附加了所有權(quán)修飾符,id和對(duì)象類型在沒有明確的指定所有權(quán)修飾符時(shí),默認(rèn)為__strong修飾符,上面代碼實(shí)質(zhì)上跟下面的代碼一致
__strong NSObject *obj1 = [[NSObject alloc]init];
__strong id obj2 = obj1;
通過__strong修飾符,不必再次鍵入retain或者release,完美地滿足了“引用計(jì)數(shù)內(nèi)存管理的思考方式”:</p>
自己生成的對(duì)象,自己持有
非自己生成的對(duì)象,自己也能持有
不再需要自己持有的對(duì)象時(shí)釋放
非自己持有的對(duì)象無法釋放
事列代碼
NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
輸出為:<p>
obj1:<NSObject: 0x100203ab0>,obj2=<NSObject: 0x100203ab0></p>
obj1:(null),obj2=<NSObject: 0x100203ab0>
__weak修飾符
看起來好像通過 __strong 修飾符編譯器就能完美地進(jìn)行內(nèi)存管理,大師遺憾的是,僅通過__strong修飾符時(shí)不能解決有些重大問題的。
這里提到的重大問題就是引用計(jì)數(shù)試內(nèi)存管理中必然會(huì)發(fā)生的“循環(huán)引用”的問題
例如前面出現(xiàn)的帶有__strong修飾符的成員變量在持有對(duì)象時(shí),很容易發(fā)生循環(huán)引用
@interface Test : NSobject {
id __strong _obj;
}
- (void)setObj:(id)obj;
@end
@implementation Test
- (void)setObj:(id)obj {
_obj = obj;
}
@end
以下就為循環(huán)引用
int main() {
id test1 = [[Test alloc]init];/*對(duì)象a*/
//test1持有Test對(duì)象a的強(qiáng)引用
id test2 = [[Test alloc]init];/*對(duì)象b*/
//test2持有Test對(duì)象b的強(qiáng)引用
[test1 setObj:test2];
/*
test1對(duì)象a的obj成員變量持有test2對(duì)象b的強(qiáng)引用
此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為Test對(duì)象a的_obj和test2
*/
[test2 setObj:test1];
/*
test2對(duì)象b的obj成員變量持有test2對(duì)象a的強(qiáng)引用
此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為Test對(duì)象b的_obj和test1
*/
/*
因?yàn)閠est1變量超出其作用域,強(qiáng)引用失效,所以自動(dòng)釋放對(duì)象a
因?yàn)閠est2變量超出其作用域,強(qiáng)引用失效,所以自動(dòng)釋放對(duì)象b
此時(shí),持有test對(duì)象a的強(qiáng)引用的變量為test2對(duì)象b的_obj
此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為test1對(duì)象a的_obj
內(nèi)存泄漏出現(xiàn)了,兩個(gè)對(duì)象超出作用域都沒有被釋放
*/
return 0;
}
下面這種情況,雖然只有一個(gè)對(duì)象,但在該對(duì)象持有其自身是也發(fā)生了循環(huán)引用
int main(){
id test = [[Test alloc]init];
[test setObj:test];
//對(duì)自身的強(qiáng)引用,自引用
return 0;
}
__weak id obj = [[NSObject alloc]init];
//實(shí)際上這樣的代碼是會(huì)報(bào)警告
//此代碼將自己生成的并吃藕的對(duì)象賦值給附有 __weak修飾符的變量 obj。即變量obj持有對(duì)持有對(duì)象的弱引用,因此,為了不以自己持有的狀態(tài)來保存自己生成并持有的對(duì)象,生成的對(duì)象會(huì)立即被釋放。編譯器對(duì)此會(huì)給出警告。如果想下面這樣,將對(duì)象賦值給一個(gè)strong修飾符的變量后再賦值給附有weak修飾符的變量,就不會(huì)發(fā)生警告了。
{
id __strong obj0 = [[NSObject alloc]init];
id __weak obj1 = obj0;
}
因?yàn)閹_weak修飾符的變量不持有對(duì)象,所以超出其變量作用域是,對(duì)象即唄釋放。如果像下面這樣將先前可能發(fā)生循環(huán)引用的類成員變量改成附有__weak修飾符的成員變量的話,該現(xiàn)象則可以避免
@interface Test : NSObject
{
id __weak _obj;
}
@end
__weak修飾符還有另一有點(diǎn)。在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄,則此引用將自動(dòng)失效且處于nil被賦值狀態(tài)(空弱引用)。即下代碼
NSObject *obj1 = [[NSObject alloc]init];
__weak NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
輸出結(jié)果為
obj1:<NSObject: 0x100202e50>,obj2=<NSObject: 0x100202e50>
obj1:(null),obj2=(null)
使用__weak修飾符可避免循環(huán)引用,通過檢查附有__weak修飾符變量
__weak修飾符智能用于iOS5以上及OS XLion以上版本的應(yīng)用程序,iOS4以及OS X Snow Leopard 的應(yīng)用程序中可以使用__unsafe_unretained來代替。
__unsafe_unretained 修飾符
__unsafe_unretained修飾符正如其名unsafe所示,不安全的所有權(quán)修飾符。盡管arc模式的內(nèi)存管理是編譯器的工作,但附有__unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象。這一點(diǎn)在使用時(shí)一定要注意<p>
id __unsafe_unretained obj = [[NSObject alloc] init];<p>
<html>
該代碼將自己生成并持有的對(duì)象賦值給附有__unsafe_unretained修飾符的變量中,雖然使用了unsafe的變量,但是編譯器并不會(huì)忽略,而是給出適當(dāng)?shù)木妗?lt;p>
</html>
warning:Assigning retained object to unsafe_unretained variable; object will be released after assignment
附有__unsafe_unretained修飾符的變量同附有__weak修飾發(fā)的變量一樣,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有,所以生成的對(duì)象會(huì)立即被釋放。到這里,__unsafe_unretained修飾符和__weak修飾符時(shí)一樣的,下面我們來看一看差異。
__unsafe_unretained id obj1 = nil;
({
id obj2 = [[NSObject alloc] init];
obj1 = obj2;
NSLog(@"obj1:%@,obj2:%@",obj1,obj2);
});
NSLog(@"obj1:%@",obj1);
代碼執(zhí)行結(jié)果為
obj1:<NSObject: 0x1003007c0>,obj2:<NSObject: 0x1003007c0>
obj1:<NSObject: 0x1003007c0>
<html>
發(fā)生了什么,來看一看
</html>
__unsafe_unretained id obj1 = nil;
({
/** 自己生成并持有對(duì)象*/
id obj2 = [[NSObject alloc] init];
/**
*因?yàn)閛bj2變量為強(qiáng)引用,所以自己持有對(duì)象
*/
obj1 = obj2;
/**雖然obj2變量賦值給了obj1,但是obj1變量即不持有對(duì)象的強(qiáng)引用也不持有弱引用
*/
NSLog(@"obj1:%@,obj2:%@",obj1,obj2);
});
/** 因?yàn)閛bj2變量超出其作用域,所以自動(dòng)釋放自己持有的對(duì)象
* 此時(shí)對(duì)象無持有者,所以廢棄該對(duì)象
*/
NSLog(@"obj1:%@",obj1);
/**
*輸出obj1比那兩表示的對(duì)象
*obj1變量表示的對(duì)象已經(jīng)被廢棄(懸垂指針)!錯(cuò)誤訪問!
*/
也就是說,最后一行的NSLog只是碰巧正常運(yùn)行而已。雖然訪問了已經(jīng)被廢棄的對(duì)象,但是應(yīng)用程序在個(gè)別運(yùn)行狀態(tài)才會(huì)崩潰。
在使用__unsafe_unretained修飾符時(shí),賦值給附有__strong修飾符的變量時(shí)有必要確保被賦值的對(duì)象確實(shí)存在。
但是,在使用前,讓我們?cè)谝怀呦胂霝槭裁葱枰褂酶接衉_unsafe_unretained修飾符變量。
比如在iOS4以及OS X Snow Leopard的應(yīng)用程序中,必須使用__unsafe_unretained修飾符來替代__weak修飾符。賦值給附有__unsafe_unretained修飾符變量的對(duì)象在通過該變量使用時(shí),如果沒有確保其實(shí)際存在,那么應(yīng)用程序就會(huì)崩潰。
__autoreleasing修飾符
ARC有效是autorelease會(huì)如何呢,不能使用autorelease方法。另外,也不能使用NSAutoreleasePool類。這樣一來,雖然autorelease無法直接使用,但實(shí)際上,ARC有效時(shí)autorelease功能起作用的
//ARC無效
{
NSAutoreleasePool *pool = [[NSAutorelease alloc]init];
id obj = [[NSObject alloc]init];
[obj autorelease];
[pool autorelease];
}
//ARC有效
{
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc]init];
}
}
指定“@autoreleasepool塊”來替代“NSAutoreleasePool類對(duì)象生成、持有以及廢棄”這一范圍。
另外,ARC有效是,要通過將對(duì)象賦值給附加了__autoreleasing修飾符的變量來替代調(diào)用autorelease方法。對(duì)象賦值給附有__autoreleasing對(duì)象修飾符的變量等價(jià)于ARC五小時(shí)調(diào)用對(duì)象那個(gè)的autorelease方法,即對(duì)象被注冊(cè)到autoreleasepool。
也就是說可以理解為,在ARC有效時(shí),用@autoreleasepool塊來替代NSAreleasePool類,用附有__autoreleasing修飾符的變量替代autorelease方法。
assign屬性修飾符跟__unsafe_unretained一致,用來修飾基礎(chǔ)數(shù)據(jù)類型