什么是ARC
ARC(自動(dòng)引用計(jì)數(shù))是一個(gè)編譯期技術(shù),介于垃圾回收(GC)和MRC之間,ARC讓程序員不再需要書寫retain/release/autorelease語句,runtime會(huì)維護(hù)一張引用計(jì)數(shù)表,編譯器會(huì)在編譯期在合適的位置自動(dòng)給用添加retain/release/autorelease,它的特點(diǎn)是自動(dòng)引用技術(shù)簡(jiǎn)化了內(nèi)存管理的難度
ARC內(nèi)存管理的思考方式:
ARC內(nèi)存管理的思考方式與MRC一致。
- 自己生成的對(duì)象,自己持有
- 非自己生成的對(duì)象,自己也能持有
- 自己持有的對(duì)象,不再需要時(shí),釋放
- 非自己持有的對(duì)象無法釋放
ARC代碼編寫規(guī)則
- 不能在程序中定義和使用:retain、release、autorelease和retainCount
- 使用@autoreleasepool代替NSAutoreleasePool
- 不用在dealloc中釋放實(shí)例變量(可以在dealloc中釋放資源),也不需要調(diào)用
[super dealloc]
所有權(quán)修飾符
id類型:用于隱藏對(duì)象類型的類名部分,類似于C語言中的void*;
ARC有效時(shí),id類型和對(duì)象類型必須加上所有權(quán)修飾符:
-
__strong修飾符 -
__weak修飾符 -
__unsafe_unretained修飾符 -
__autoreleasing修飾符
__strong修飾符
__strong修飾符是默認(rèn)的修飾符
//兩種方式是等價(jià)的
id obj = [[NSObject alloc]init];
id __strong obj = [[NSObject alloc]init]
特性:strong表示強(qiáng)引用,持有強(qiáng)引用的變量在超出其作用域時(shí)被廢棄,隨著強(qiáng)引用的失效,引用的對(duì)象會(huì)隨之釋放。
- 與自己生成并持有對(duì)象
//ARC有效
{
//自己生成,自己持有,obj為強(qiáng)引用,持有變量
id __strong obj = [[NSObject alloc]init];
//todo
.......
}
//超出作用域,強(qiáng)引用失效
//以上代碼在ARC無效下等價(jià)于
{
id obj = [[NSObject alloc]init];
//todo
......
[obj release];
}
2、取得非自己生成但持有的對(duì)象
//取得非自己生成的對(duì)象,obj為強(qiáng)引用,所以持有該對(duì)象
id __strong obj = [NSArray array];
3、附有__strong修飾符的變量可以相互賦值
//obj0持有對(duì)象A的強(qiáng)引用
id __strong obj0 = [[NSObject alloc]init];
//obj1持有對(duì)象B的強(qiáng)引用
id __strong obj1 = [[NSObject alloc]init];
//obj2不持有任何對(duì)象
id __strong obj2 = nil;
/*
* obj0持有obj1賦值的對(duì)象B的強(qiáng)引用,對(duì)象B的持有者為obj0、obj1
* obj0被賦值,原先持有的對(duì)象A的強(qiáng)引用失效,對(duì)象A的所有者不存在,因此對(duì)象A廢棄
*/
obj0 = obj1;
/*
* obj2持有obj0賦值的對(duì)象B的強(qiáng)引用
* 對(duì)象B的持有者為:obj0、obj1、obj2
*/
obj2 = obj0;
4、附有__strong修飾符的變量作為方法的參數(shù)也能正確的管理其對(duì)象的所有者
總結(jié):通過__strong修飾符,不必再次鍵入retain或release,完美地滿足了“引用計(jì)數(shù)式內(nèi)存管理的思考方式”。
__weak修飾符
__weak修飾符,提供弱引用,弱引用不能持有實(shí)例對(duì)象,可以避免循環(huán)引用
/*
* 編譯器會(huì)給警告
* obj為弱引用,并不持有對(duì)象,生成的對(duì)象會(huì)被立即釋放,所以會(huì)給警告
*/
id __weak obj = [[NSObject alloc]init];
//obj0強(qiáng)引用持有對(duì)象,obj1弱引用不持有對(duì)象
id __strong obj0 = [[NSObject alloc]init];
id __weak obj1 = obj0;
注: 在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄,則弱引用自動(dòng)失效,且處于nil被賦值的狀態(tài)(空弱引用)
__unsafe_unretained修飾符
__unsafe_unretained是不安全的修飾符,修飾的變量不屬于編譯器的內(nèi)存管理對(duì)象,同__weak修飾符一樣,不能持有實(shí)例對(duì)象
/*
* 編譯器警告,obj不持有NSObject對(duì)象
*/
id __unsafe_unretained obj = [[NSObject alloc]init];
id __unsafe_unretained obj1 = nil;
{
//obj0強(qiáng)引用NSObject對(duì)象
id __strong obj0 = [[NSObject alloc]init];
//obj1不持有NSObject對(duì)象
obj1 = obj0;
NSLog(@"A:%@",obj1);
}
/*
* obj0變量超出作用域,強(qiáng)引用失效,釋放持有變量
* obj0指向的對(duì)象被廢棄(懸垂指針),成為僵尸對(duì)象
*/
NSLog(@"B:%@",obj1);
執(zhí)行結(jié)果:
A: <NSObject: 0x753e180>
B: <NSObject: 0x753e180>
最后一行NSlog,碰巧正常運(yùn)行。因?yàn)閛bj0指向的對(duì)象已經(jīng)被廢棄,不能被訪問
為什么要有__unsafe_unretained修飾符?
在iOS4以及OS X Snow Leopard的應(yīng)用程序中,必須使用__unsafe_retained修飾符來代替__weak,__unsafe_unretained修飾的對(duì)象賦值給__strong修飾符的變量時(shí),必須保證被賦值對(duì)象確實(shí)存在。
__autoreleasing修飾符
ARC無效時(shí)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSObject alloc]init];
[obj autorelease];
[pool drain];
ARC有效時(shí),代碼寫為
//ARC指定“@autoreleasepool塊”來替代“NSAutoReleasePool類對(duì)象生成、持有以及廢棄”這一范圍
@autoreleasepool {
//添加__autoreleasing修飾符來代替調(diào)用autorelease方法
id __autoreleasing obj = [[NSObject alloc]init];
}
非顯示使用__autorelease的情況
__autoreleasing和__strong一樣通常都不會(huì)顯示添加。
- ARC有效時(shí),在取得非自己生成并持有的對(duì)象時(shí),編譯器會(huì)檢查方法名是否以
alloc/new/copy/mutableCopy開始,如果不是則自動(dòng)將返回的對(duì)象值注冊(cè)到autoreleasepool中。
@autoreleasepool {
// 變量為強(qiáng)引用,所以自己持有對(duì)象
//但編譯器判斷其方法名后,自動(dòng)將其注冊(cè)到autoreleasepool
id __strong obj = [NSMutableArray array];
}
- 非
alloc/new/copy/mutableCopy開頭的非自己生成但持有的方法中定義的對(duì)象
+ (id)array
{
//obj生成并持有對(duì)象,強(qiáng)引用
id obj = [[NSMutableArray alloc]init];
//強(qiáng)引用在obj超出作用域后會(huì)釋放對(duì)象,但是該對(duì)象又作為返回值不能被釋放,所以編譯器會(huì)自動(dòng)注冊(cè)到autoreleasepool中
return obj;
}
//得到的是注冊(cè)到autoreleasepool中的對(duì)象
id obj = [NSMutableArray array];
- 訪問
__weak修飾的對(duì)象,實(shí)際上訪問的是注冊(cè)到autoreleasepool中的對(duì)象
id __weak obj1 = obj0;
NSLog(@"class = %@", [obj1 class])
//等價(jià)于
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@", [tmp class]);
__weak修飾的對(duì)象為弱引用,在訪問時(shí)可能會(huì)隨時(shí)被廢棄,只要把對(duì)象注冊(cè)到autoreleasepool中,就能在@autoreleasepool塊結(jié)束前確保對(duì)象存在
- id的指針或者NSObject的指針沒有顯示指定時(shí),會(huì)被默認(rèn)被附上
__autoreleasing修飾符
//id的指針
id *obj;
id __autoreleasing *obj;
//對(duì)象的指針
NSError **error;
NSError * __autoreleasing *error;
- (BOOL)performOperationWithError:(NSError **)error;
//error為對(duì)象指針,所以默認(rèn)修飾符是__autoreleasing;
- (BOOL)performOperationWithError:(NSError *__autoreleasing *)error {
*error = [[NSError alloc] initwithDomain:MyApplication code:errorCode userInfo:nil];
return NO
}
這里- (BOOL)performOperationWithError:(NSError **)error;與除alloc/new/copy/mutableCopy外的其他非自己生成但持有的方法一樣,{}里[[NSError alloc] initwithDomain:MyApplication code:errorCode userInfo:nil]生成的對(duì)象因?yàn)槌鲎饔糜蚝髸?huì)釋放對(duì)象,所以編譯器會(huì)將其放入到autoreleasepool中,所以這里的alloc方法生成的對(duì)象(理論上是__strong修飾的對(duì)象)能夠正常賦值給__autoreleasing修飾的*error
注:賦值給對(duì)象指針時(shí),所有權(quán)修飾符必須一致
//默認(rèn)是__strong
NSError *error = nil;
//默認(rèn)是__autoreleasing
NSError **pError = &error; //報(bào)錯(cuò)
NSError * __autoreleasing *pError1 = &error; //不報(bào)錯(cuò)
//不報(bào)錯(cuò)
NSError _strong *error = nil;
BOOL result = [obj performOperationWithError:&error];
上面的error是__strong修飾的,但是performOperationWithError:里的參數(shù)是__autoreleasing修飾的,但是沒有因修飾符不一致而報(bào)錯(cuò),原因是編譯器做了如下轉(zhuǎn)換:
NSError __strong *error = nil;
NSError __autoreleasing *tmp = error;
BOOL result = [obj performOperationWithError:&tmp];
注:顯示指定
__autoreleasing時(shí)要注意,對(duì)象變了要為自動(dòng)變量(包括局部變量、函數(shù)以及方法參數(shù))
總結(jié):
-
__strong、__weak以及__autoreleasing修飾的變量初始值默認(rèn)為nil - 自己生成并持有的對(duì)象,即以alloc/new/copy/mutableCopy開頭的方法生成的對(duì)象,編譯器默認(rèn)給變量添加修飾符
__strong - 非自己生成但持有的對(duì)象,編譯器默認(rèn)給變量添加修飾符
__autoreleasing - 非自己生成但持有對(duì)象的方法中返回的值默認(rèn)是
__autoreleasing - id指針和對(duì)象指針默認(rèn)是
__autoreleasing -
__weak用于避免循環(huán)引用,在對(duì)象廢棄時(shí),自動(dòng)降變量置為nil -
__unsafe_unretained在iOS4中用來替代__weak,但是在對(duì)象被廢棄時(shí),不會(huì)將變量置為nil,因此在將__unsafe_unretained修飾的變量賦值給__strong修飾的變量時(shí)要確保對(duì)象是否存在 - 修飾符是ARC時(shí)才用的,MRC沒有修飾符
- 修飾符用于實(shí)例變量、自動(dòng)變量等變量的聲明,不能用于屬性
參考資料:
- Objective-C高級(jí)編程iOS與OS X多線程和內(nèi)存管理