ARC的內(nèi)存管理方式


什么是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ī)則

  1. 不能在程序中定義和使用:retain、release、autorelease和retainCount
  2. 使用@autoreleasepool代替NSAutoreleasePool
  3. 不用在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ì)隨之釋放。

  1. 與自己生成并持有對(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)存管理
最后編輯于
?著作權(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)容