(轉(zhuǎn))一篇文章徹底弄清ARC始末

自動(dòng)引用計(jì)數(shù)(ARC)是編譯器的一個(gè)特色锈锤,提供了Objective-C對(duì)象的自動(dòng)內(nèi)存管理機(jī)制吧兔。比起不得不考慮retain和release操作鳖链,ARC讓你更加專注于應(yīng)用中那些有趣的代碼葛超,如對(duì)象圖,對(duì)象關(guān)系祝蝠。

摘要(Summary)

通過(guò)在編譯期添加代碼的方式音诈,ARC保證所有對(duì)象按需存在幻碱,按需釋放。從概念上來(lái)講细溅,它與手動(dòng)引用計(jì)數(shù)(參見(jiàn) Advanced Memory Management Programming Guide)有著相同的內(nèi)存管理約定褥傍,二者都會(huì)為你添加合適的內(nèi)存管理方法調(diào)用。 為了編譯器能生成正確的代碼谒兄,ARC限定了你可以使用的一些方法摔桦,以及toll-free橋接的使用方式(參見(jiàn) “Toll-Free Bridged Types”).與此同時(shí)ARC還為對(duì)象引用(object references )和聲明式屬性(declared properties)引進(jìn)了新的生命周期限定符(lifetime qualifiers )社付。 ARC適用于OS X v10.6和v10.7(64位)下的Xcode4.2承疲,以及IOS 4和IOS5.OS X v10.6和IOS4不支持弱引用。Xcode提供了自動(dòng)化的工具鸥咖,完成ARC轉(zhuǎn)換過(guò)程中需要手工操作的部分(例如刪除retain和release方法調(diào)用)燕鸽,并且?guī)椭阍?遷移過(guò)程中不能自動(dòng)完成的操作(選擇Edit->Refactor->Convert to Objective-C ARC)。遷移工具將工程中的所有文件使用ARC進(jìn)行轉(zhuǎn)換啼辣。如果你覺(jué)得在某些文件中使用手動(dòng)引用計(jì)數(shù)會(huì)更方便啊研,那么你也可以選擇在單獨(dú)文件中使用ARC。
參見(jiàn):
Advanced Memory Management Programming Guide
Memory Management Programming Guide for Core Foundation

ARC概述(ARC Overview)

ARC會(huì)評(píng)估對(duì)象所需的生命期鸥拧,并會(huì)在編譯期為你自動(dòng)插入合適的內(nèi)存管理調(diào)用方法党远,取代之前你不得不考慮何時(shí)需要使用 retain, release以及 autorelease的操作方式富弦。編譯器也會(huì)為你生成合適的 dealloc方法沟娱。總的來(lái)說(shuō)腕柜,如果你僅使用ARC济似,傳統(tǒng)的Cocoa命名規(guī)范只會(huì)在你需要與使用手動(dòng)引用計(jì)數(shù)的代碼交互時(shí)才是重要的。一個(gè)完整正確的Person類(lèi)的實(shí)現(xiàn)看起來(lái)可能是這樣的:

@interface Person : NSObject   
@property NSString *firstName;   
@property NSString *lastName;   
@property NSNumber *yearOfBirth;   
@property Person *spouse;   
@end   
@implementation Person   
@end  

(對(duì)象屬性(properties)默認(rèn)是強(qiáng)類(lèi)型的( strong); strong屬性描述于 “ARC Introduces New Lifetime Qualifiers.”)
使用ARC盏缤,你可以如下實(shí)現(xiàn)一個(gè) contrived方法:

- (void)contrived   
{   
    Person *aPerson = [[Person alloc] init];   
    [aPerson setFirstName:@"William"];   
    [aPerson setLastName:@"Dudney"];   
    [aPerson setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];   
    NSLog(@"aPerson: %@", aPerson);   
}   

ARC會(huì)維護(hù)內(nèi)存管理砰蠢,所以 Person和 NSNumber對(duì)象都不會(huì)泄露。
你也可以安全的實(shí)現(xiàn) Person類(lèi)中的 takeLastnameFrom:方法唉铜。

- (void)takeLastNameFrom:(Person *)person {   
    NSString *oldLastname = [self lastName];   
    [self setLastName:[person lastName]];   
    NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);   
}  

ARC保證 oldLastName在 NSLog語(yǔ)句之前不會(huì)被釋放台舱。
【ARC強(qiáng)制執(zhí)行新規(guī)則】(ARC Enforces New Rules)
為了正常運(yùn)轉(zhuǎn),ARC使用了一些在使用其它編譯模式下沒(méi)有的新規(guī)則潭流。這些規(guī)則意在提供完全可靠的內(nèi)存管理模型竞惋;在某些情況下,它們僅使用最佳實(shí)踐方法幻枉,在其它情況下碰声,它們僅簡(jiǎn)化你的代碼或明顯的做出推論告知你不需要處理內(nèi)存管理。如果
你違反了這些規(guī)則熬甫,你會(huì)馬上得到一個(gè)編譯期錯(cuò)誤胰挑,而不是在運(yùn)行期可能會(huì)顯現(xiàn)的一個(gè)狡猾的bug。
1.不能顯示的調(diào)用dealloc,實(shí)現(xiàn)或調(diào)用 retain瞻颂, release豺谈, retainCount,或 autorelease贡这。
同樣也不要能使用 @selector(retain), @selector(release), 等等類(lèi)似的選擇器茬末。
如果你需要管理資源而不是釋放實(shí)例變量,那你可以實(shí)現(xiàn) dealloc方法盖矫。你不需要(事實(shí)上你不能)釋放實(shí)例變量丽惭,但你可能需要在系統(tǒng)類(lèi)和其它的未使用ARC代碼中調(diào)用 [systemClassInstance setDelegate:nil] 方法。
在ARC中自定義的 dealloc方法不要調(diào)用 [super dealloc]方法(它實(shí)際上會(huì)導(dǎo)致編譯器錯(cuò)誤)辈双。到super的鏈?zhǔn)秸{(diào)用是自動(dòng)的并且是編譯器強(qiáng)制執(zhí)行的责掏。
你仍可以在Core Foundation樣式的的對(duì)象上,使用 CFRetain湃望, CFRelease换衬,和其它相關(guān)的函數(shù)。
2.你不能使用 NSAllocateObject 或 NSDeallocateObject
你使用 alloc來(lái)創(chuàng)建對(duì)象证芭;運(yùn)行時(shí)系統(tǒng)會(huì)注意釋放這些對(duì)象瞳浦。
3.你不能在C語(yǔ)言結(jié)構(gòu)體中使用對(duì)象指針。 與其使用一個(gè)結(jié)構(gòu)體( struct)废士,不如創(chuàng)建一個(gè)Objective-C類(lèi)來(lái)管理數(shù)據(jù)叫潦。

  1. id與 void*之間不能隨意轉(zhuǎn)換
    你必須使用特定的類(lèi)型轉(zhuǎn)換來(lái)告訴編譯器對(duì)象的生命周期。你需要在Objective-C對(duì)象和以函數(shù)參數(shù)傳入的Core Foundation類(lèi)型值之間進(jìn)行這樣的轉(zhuǎn)換湃密。有關(guān)詳情诅挑,參見(jiàn) “Managing Toll-Free Bridging”。
    5.你不能使用 NSAutoreleasePool對(duì)象
    ARC 提供了 @autoreleasepool來(lái)代替泛源。這比 NSAutoreleasePool更高效拔妥。
    6.你不能使用內(nèi)存區(qū)(memory zones)。
    再也沒(méi)有使用 NSZone的必要了——現(xiàn)代的Obj-C運(yùn)行時(shí)會(huì)永遠(yuǎn)忽略它达箍。
    為了允許與自動(dòng)retain-release的代碼進(jìn)行交互没龙,ARC在方法命名上加上了一個(gè)約束:
    你不能以 new為開(kāi)頭命名一個(gè)訪問(wèn)器的名字。這反過(guò)來(lái)意味著你不必聲明一個(gè)以new開(kāi)頭的屬性缎玫,除非你指定一個(gè)不同名稱的getter方法:
// Won't work:   
@property NSString *newTitle;   
// Works:   
@property (getter=theNewTitle) NSString *newTitle; 

【ARC引入新的生命周期限定符】(ARC Introduces New Lifetime Qualifiers)
ARC為對(duì)象引入了幾種新的生命周期限定符硬纤,以及弱類(lèi)型引用( weak references)。弱類(lèi)型應(yīng)用不會(huì)延長(zhǎng)它所指向?qū)ο蟮纳芷谠吣ィ坏?duì)象沒(méi)有強(qiáng)引用指向?qū)ο髸r(shí)筝家,弱引用會(huì)自動(dòng)變?yōu)?nil。
在程序中邻辉,你應(yīng)該充分利用這些限定符來(lái)管理對(duì)象圖溪王。尤其是腮鞍,ARC不能阻止強(qiáng)引用循環(huán)(strong reference cycles,之前稱為retain cycles莹菱,參見(jiàn)“Practical Memory Management”)的發(fā)生移国。審慎的使用弱類(lèi)型關(guān)系會(huì)幫助你避免創(chuàng)建強(qiáng)引用循環(huán)。
【Property的屬性】(Property Attributes)
weak和 strong關(guān)鍵字是新引入的property聲明屬性道伟,如下例所示:

// 以下聲明等價(jià)于 @property(retain) MyClass *myObject;   
@property(strong) MyClass *myObject;   
// 以下聲明等價(jià)于"@property(assign) MyClass *myObject;"   
// 不同之處在于如果MyClass的實(shí)例變量釋放時(shí)迹缀,其屬性值會(huì)設(shè)置為nil,而不會(huì)保存為一個(gè)野指針   
@property(weak) MyClass *myObject;   

ARC下蜜徽,對(duì)象類(lèi)型的聲明默認(rèn)為 strong
【變量限定符】(Variable Qualifiers)
你可以向使用其它變量的限定符那樣祝懂,比如說(shuō), const娜汁,來(lái)使用以下生命周期限定符嫂易,

__strong

__weak

__unsafe_unretained  

__autoreleasing   

__strong是默認(rèn)的兄朋。只要有強(qiáng)類(lèi)型指針指向一個(gè)對(duì)象掐禁,那么該對(duì)象會(huì)一直”生存“下去。
__weak表明一個(gè)不會(huì)維持所持對(duì)象生命期的引用颅和。當(dāng)沒(méi)有強(qiáng)引用指向該對(duì)象時(shí)傅事,弱引用會(huì)設(shè)置為nil。
__unsafe_unretained指定一個(gè)引用峡扩,該引用不會(huì)維持所持對(duì)象的生命期蹭越,并且在沒(méi)有強(qiáng)引用指向?qū)ο髸r(shí)也不會(huì)設(shè)置為nil。如果它所指向的對(duì)象已經(jīng)被釋放教届,那么它會(huì)成為一個(gè)野指針响鹃。
__autoreleasing 用以指示以引用(id*)傳入的參數(shù)并在retun后自動(dòng)釋放。
你應(yīng)該正確的修飾變量案训。在對(duì)象變量的聲明時(shí)使用限定符的正確格式為:


ClassName * qualifier variableName;  

例如:

MyClass * __weak myWeakReference;   
MyClass * __unsafe_unretained myUnsafeReference;  

從技術(shù)上講其它的變體寫(xiě)法都是錯(cuò)誤的买置,但編譯器都會(huì)”寬恕“它們。要弄清該問(wèn)題强霎,參見(jiàn)http://cdecl.org/.
在堆棧上使用 __weak變量時(shí)要當(dāng)心忿项。考慮以下的例子:

NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];   
NSLog(@"string: %@", string);  

雖然 string是在初始化賦值之后使用城舞,但是在賦值的時(shí)候并沒(méi)有其它強(qiáng)引用指向字符串對(duì)象轩触;因此字符串對(duì)象會(huì)馬上釋放掉。log語(yǔ)句顯示 stirng的值為 null家夺。(編譯器對(duì)這種情況會(huì)提示警告)
你也要注意通過(guò)引用方式傳入的對(duì)象脱柱。以下代碼會(huì)正常運(yùn)轉(zhuǎn)

NSError *error;   
BOOL OK = [myObject performOperationWithError:&error];   
if (!OK) {   

而error的聲明是隱式的:

NSError * __strong e;  

方法的聲明通常是:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;   

因此編譯器會(huì)重寫(xiě)代碼:

NSError * __strong error;   
NSError * __autoreleasing tmp = error;   
BOOL OK = [myObject performOperationWithError:&tmp];   
error = tmp;   
if (!OK) {   

本地變量聲明( __strong)和參數(shù)( __autoreleasing)之間的區(qū)別導(dǎo)致編譯器創(chuàng)建臨時(shí)變量。在獲取__strong變量的地址時(shí)你可以通過(guò)將參數(shù)聲明為 id __storng*來(lái)獲得其原始指針拉馋≌ノ或者你可以將變量聲明為 __autoreleasing掸茅。
【使用生命周期限定符來(lái)避免強(qiáng)類(lèi)型循環(huán)引用】(Use Lifetime Qualifiers to Avoid Strong Reference Cycles)
你可以使用生命周期限定符來(lái)避免強(qiáng)類(lèi)型循環(huán)引用。例如柠逞,通常如果你的對(duì)象圖形成于父-子層級(jí)結(jié)構(gòu)中昧狮,父對(duì)象需要引用它的子對(duì)象,反之亦然板壮, 那么你構(gòu)造parent-to-child的關(guān)系為強(qiáng)類(lèi)型逗鸣,child-to-parent的關(guān)系為弱類(lèi)型。其它情況可能會(huì)更加微妙绰精,尤其是在涉及到 block對(duì)象時(shí)撒璧。
在手動(dòng)引用技術(shù)的模式下, __block id x,不會(huì)保留x笨使。在ARC模式下卿樱, __block id x;默認(rèn)會(huì)保留x(就像其它值一樣)。為了在ARC下達(dá)到手動(dòng)引用技術(shù)的效果硫椰,你可以使用 __unsafe__unretained __block id x ;然而繁调,就像 __unsafe_unretained名字蘊(yùn)含的那樣,擁有一個(gè)未保留變量(non-retained variable)是危險(xiǎn)的(因?yàn)樗赡苁且粋€(gè)野指針)靶草,因此最好不要使用蹄胰。有兩個(gè)更好的選擇是要么使用 __weak(如果你不需要支持IOS4或OS X v10.6),要門(mén)設(shè)置 __block值nil來(lái)打破循環(huán)引用奕翔。
以下的代碼片段說(shuō)明了在手動(dòng)引用技術(shù)下時(shí)常會(huì)使用的模式裕寨。

MyViewController *myController = [[MyViewController alloc] init…];   
// ...   
myController.completionHandler =  ^(NSInteger result) {   
   [myController dismissViewControllerAnimated:YES completion:nil];   
};   
[self presentViewController:myController animated:YES completion:^{   
   [myController release];   
}];   

如上所屬,你可以使用 __block限定符來(lái)替代派继,并在 completion處理方法中將myController的值設(shè)置為nil:

MyViewController * __block myController = [[MyViewController alloc] init…];   
// ...   
myController.completionHandler =  ^(NSInteger result) {   
    [myController dismissViewControllerAnimated:YES completion:nil];   
    myController = nil;   
}; 

或者宾袜,你可以使用一個(gè)臨時(shí)的 __weak變量。以下代碼列舉了一個(gè)簡(jiǎn)單的實(shí)現(xiàn):

MyViewController *myController = [[MyViewController alloc] init…];   
// ...   
MyViewController * __weak weakMyViewController = myController;   
myController.completionHandler =  ^(NSInteger result) {   
    [weakMyViewController dismissViewControllerAnimated:YES completion:nil];   
}; 

而對(duì)于特殊的循環(huán)驾窟,你應(yīng)該使用:

MyViewController *myController = [[MyViewController alloc] init…];   
// ...   
MyViewController * __weak weakMyController = myController;   
myController.completionHandler =  ^(NSInteger result) {   
    MyViewController *strongMyController = weakMyController;   
    if (strongMyController) {   
        // ...   
       [strongMyController dismissViewControllerAnimated:YES completion:nil];   
        // ...   
    }   
    else {   
        // Probably nothing...   
    }   
};  

某些情況下如果一個(gè)類(lèi)不兼容__ weak,你可以使用 __unsafe_unretained庆猫。但這也不適用于特殊的循環(huán),因?yàn)樵趯?shí)際問(wèn)題中驗(yàn)證 __unsafe_unretained指針是否仍有效或是否仍指向相同的對(duì)象纫普,是非常困難或根本不可能實(shí)現(xiàn)的阅悍。
【ARC使用新聲明來(lái)管理自動(dòng)釋放池】(ARC Uses a New Statement to Manage Autorelease Pools)
使用ARC,你不能直接使用NSAutoreleasePool類(lèi)來(lái)管理自動(dòng)釋放池昨稼。相反节视,你需要使用 @autoreleasepool塊:

@autoreleasepool {   
     // Code, such as a loop that creates a large number of temporary objects.   
}   

這個(gè)簡(jiǎn)單的結(jié)構(gòu)讓編譯器來(lái)判斷引用計(jì)數(shù)的狀態(tài)。在入口假栓,自動(dòng)釋放池會(huì)被push寻行。在正常退出( break,return匾荆,goto拌蜘,fall-through杆烁,dengd)自動(dòng)釋放池會(huì)被pop。出于對(duì)已有代碼兼容性的考慮简卧,如果因異常導(dǎo)致退出兔魂,自動(dòng)釋放池將不會(huì)被pop。
該語(yǔ)法在所有Objecti-C模式下都可用举娩。相比使用 NSAutoreleasePool類(lèi)來(lái)說(shuō)析校, @autoreleasepool更加高效;因此鼓勵(lì)你在使用 NSAutoreleasePool的地方使用 @autoreleasepool铜涉。
【跨平臺(tái)下Outlets管理保持一致的模式】(Patterns for Managing Outlets Become Consistent Across Platforms)
ARC下的IOS和OS X中聲明outlets的模式已經(jīng)發(fā)生變化智玻,并且在跨平臺(tái)下保持一致性。你通常應(yīng)該接受的是:outlets應(yīng)該是 weak的芙代,除了那些在nib文件(或storyboard屏)中File‘s Owner指向的頂級(jí)對(duì)象應(yīng)該是 strong的吊奢。
Resource Programming Guide.中的 ”Nib Files“做出了全面的解釋。
【堆變量會(huì)初始化為nil】(Stack Variables Are Initialized with nil)
使用ARC纹烹,strong页滚,weak,以及autoreleasing的堆變量現(xiàn)在會(huì)隱式的初始化為nil滔韵,例如:

- (void)myMethod {   
    NSString *name;   
    NSLog(@"name: %@", name);   
}   

輸出的 name的值為null而不可能是程序崩潰逻谦。
【使用編譯器指令來(lái)啟用或禁用ARC】(Use Compiler Flags to Enable and Disable ARC)
使用 -fobjc-arc編譯指令來(lái)啟動(dòng)ARC。如果在某些文件中使用手動(dòng)引用技術(shù)對(duì)你來(lái)說(shuō)更方便些陪蜻,你可以選擇在單獨(dú)的文件上使用ARC。對(duì)于默認(rèn)使用ARC的工程贱鼻,你可以使用 fno-objc-arc編譯指令來(lái)禁用某個(gè)文件的ARC宴卖。
Xcode4.2及更高版本,OS X v10.6及更高版本(64為應(yīng)用)邻悬,IOS4版本或更高版本支持ARC症昏。OS X v10.6和IOS4不支持弱引用。Xcode4.1及早期版本的Xcode不支持ARC父丰。
【管理Toll-Free橋接】(Managing Toll-Free Bridging)
在許多Cocoa應(yīng)用中肝谭,你可以使用Core Foundation樣式的對(duì)象,不管是來(lái)自于Core Foundation框架本身(例如蛾扇, CFArrayRef 或 CFMutableDictionaryRef)攘烛,還是來(lái)自于諸如Core Graphics一樣采用Core Foundation約定的框架(你可能使用向 CGColorSpaceRef andCGGradientRef樣式的類(lèi)型)。
編譯器不會(huì)自動(dòng)管理Core Foundation對(duì)象的生命周期镀首;你必須調(diào)用符合Core Founda內(nèi)存管理規(guī)則的CFRetain和CFRelease方法(或者合適的特殊類(lèi)型變體)來(lái)釋放對(duì)象坟漱。(參見(jiàn) Memory Management Programming Guide for Core Foundation)
如果你在Objective-C對(duì)象和Core Foundation樣式對(duì)象間執(zhí)行類(lèi)型轉(zhuǎn)換,你要告訴編譯器對(duì)象的歸屬語(yǔ)義更哄,不管對(duì)象是使用類(lèi)型轉(zhuǎn)換(定義在objc/runtime.h)或Core Foundation樣式的宏(定義在NSObject.h)芋齿。
__bridge在Objective-C和Core Foundation間的指針轉(zhuǎn)換不附加對(duì)象的所有權(quán)腥寇。
__bridge_retained或 CFBridgingRetain將Objectiv-C指針轉(zhuǎn)換為Core Foundation指針,并且將對(duì)象的所有權(quán)轉(zhuǎn)給你觅捆。你負(fù)責(zé)調(diào)用CFRelease或相關(guān)的函數(shù)來(lái)放棄對(duì)象的所有權(quán)赦役。
__bridge_transfer或 CFBridgingRelease將一個(gè)非Objective-C指針轉(zhuǎn)移到Objective-C指針,并將對(duì)象所有權(quán)轉(zhuǎn)交給ARC栅炒。ARC負(fù)責(zé)放棄對(duì)象的所有權(quán)扩劝。
例如,如果你有這樣的代碼:

- (void)logFirstNameOfPerson:(ABRecordRef)person {   
    NSString *name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);   
    NSLog(@"Person's first name: %@", name);   
    [name release];   
}   

你應(yīng)該替代為:

- (void)logFirstNameOfPerson:(ABRecordRef)person {   
    NSString *name = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));   
    NSLog(@"Person's first name: %@", name);   
}   

編譯器處理Cocoa方法返回的CF對(duì)象】(The Compiler Handles CF Objects Returned From Cocoa Methods)
編譯器了解Objective-C的那些沿用Cocoa命名規(guī)則返回Core Foundation類(lèi)型的方法(參見(jiàn)Advanced Memory Management Programming Guide)职辅。例如棒呛,編譯器知道,在IOS中域携,UIColor的 CGColor方法返回的CGColor不會(huì)被擁有簇秒。你仍需使用合適的類(lèi)型轉(zhuǎn)換,如下例所示:

NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];   
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];  

【使用所有權(quán)關(guān)鍵字對(duì)函數(shù)參數(shù)進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換】(Cast Function Parameters Using Ownership Keywords)
函數(shù)調(diào)用中在Objective-C和Core Foundation對(duì)象之間進(jìn)行轉(zhuǎn)換秀鞭,你要告訴編譯器被傳入的對(duì)象的所有權(quán)語(yǔ)義趋观。Core Foundation的所有權(quán)規(guī)則在Core Foundation的內(nèi)存管理規(guī)則中都已近說(shuō)明了(參見(jiàn) Memory Management Programming Guide for Core Foundation);Objective-C對(duì)象的規(guī)則在 Advanced Memory Management Programming Guide.有說(shuō)明锋边。
在下面的代碼片段中皱坛,傳入 CGGradientCreateWithColors函數(shù)的數(shù)組需要一個(gè)合適的類(lèi)型轉(zhuǎn)換。 arrayWithObjects:方法返回的對(duì)象的所有權(quán)不會(huì)傳給函數(shù)豆巨,因此需要用 __bridge轉(zhuǎn)換徒恋。

NSArray *colors = <#An array of colors#>;   
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);   
以下方法實(shí)現(xiàn)中展示了代碼片段。要注意的是遵從Core Foundation內(nèi)存管理規(guī)則的Core Foundation內(nèi)存管理函數(shù)的用法飞苇。
[objc] view plain copy 在CODE上查看代碼片派生到我的代碼片
- (void)drawRect:(CGRect)rect {  
    CGContextRef ctx = UIGraphicsGetCurrentContext();  
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();  
    CGFloat locations[2] = {0.0, 1.0};  
      
    NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];  
    [colors addObject:(id)[[UIColor lightGrayColor] CGColor]];  
      
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);  
    CGColorSpaceRelease(colorSpace);  // Release owned Core Foundation object.  
      
    CGPoint startPoint = CGPointMake(0.0, 0.0);  
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds));  
    CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint,  
                                kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);  
    CGGradientRelease(gradient);  // Release owned Core Foundation object.  
}  

【工程轉(zhuǎn)換時(shí)的常見(jiàn)問(wèn)題】(Common Issues While Converting a Project)
當(dāng)遷移已有工程時(shí)澎语,可能你會(huì)遇到許多問(wèn)題。這里列舉一些常見(jiàn)問(wèn)題萍膛,以及解決辦法吭服。
1.不能調(diào)用 retain, release蝗罗,或 autorelease艇棕。
這是一個(gè)特殊點(diǎn)。你也不能寫(xiě):
while ([x retainCount]) { [x release]; }
2.不能調(diào)用 dealloc
如果你實(shí)現(xiàn)了一個(gè)單例對(duì)象或在init方法中替換一個(gè)對(duì)象串塑,你通常會(huì)調(diào)用 dealloc方法沼琉。對(duì)于單例對(duì)象,使用共享實(shí)例模式拟赊。在 init方法中刺桃,你不再需要調(diào)用dealloc,因?yàn)閷?duì)象在你重寫(xiě) self時(shí)會(huì)被釋放掉吸祟。
3.不能使用 NSAutoreleasePool對(duì)象
使用新的 @autoreleasepool{}結(jié)構(gòu)代替瑟慈。這在你的自動(dòng)釋放池中強(qiáng)制使用block結(jié)構(gòu)桃移,并且比 NSAutoreleasePool要快六倍。即使在非ARC的代碼中 @autoreleasepool也能工作葛碧,因?yàn)?@autoreleasepool較 NSAutoreleasePool要快得多借杰,所以許多舊的”性能hacks“會(huì)無(wú)條件的被 @autoreleasepool所替代。
遷移器只處理 NSAutoreleasePool的簡(jiǎn)單用法进泼,不能處理復(fù)雜條件下的情況蔗衡,或者一個(gè)定義在新的 @autoreleasepool結(jié)構(gòu)內(nèi)部的變量并在其之外使用的情況。
4.ACR需要你將在init方法中 [super init]的結(jié)果賦值給 self乳绕。
下面的代碼在ARC的init方法中是不合法的:

[super init];  

簡(jiǎn)單的修正是做如下變換

self = [super init];   

更全面的改法是在繼續(xù)之前檢測(cè)結(jié)果是否為nil:

self = [super init];   
if (self) {

5.不能實(shí)現(xiàn)自定義的 retain和 release方法
實(shí)現(xiàn)自定義的 retain和 release方法會(huì)打破弱引用绞惦。有幾種常見(jiàn)原因是想要提供提供自定義實(shí)現(xiàn)的:
性能
請(qǐng)不要再這么做了; NSObject的 retain和 release現(xiàn)在更加快捷洋措。如果你仍發(fā)現(xiàn)問(wèn)題济蝉,請(qǐng)?zhí)峤籦ug。
實(shí)現(xiàn)自定義的弱指針系統(tǒng)
使用 __weak代替
實(shí)現(xiàn)單例類(lèi)
使用共享示例代替菠发⊥趼耍或使用類(lèi)方法類(lèi)替代實(shí)例方法,這避免了一直都要去分配對(duì)象空間的操作滓鸠。
6."Assigned"實(shí)例變量成為了strong類(lèi)型
在ARC之前雁乡,實(shí)例變量是非擁有引用(non-owning redernces)——直接將對(duì)象賦值給實(shí)例變量不會(huì)擴(kuò)展對(duì)象的生命周期。為了令屬性成為強(qiáng)類(lèi)型糜俗,你通常只要實(shí)現(xiàn)或合成訪問(wèn)器方法踱稍,它們會(huì)調(diào)用合適的內(nèi) 存管理方法;相比較而言吩跋,你可能已經(jīng)如下例子中實(shí)現(xiàn)了訪問(wèn)器方法來(lái)維護(hù)一個(gè)弱類(lèi)型屬性寞射。

@interface MyClass : Superclass {   
    id thing; // Weak reference.   
}   
// ...   
@end   
@implementation MyClass   
- (id)thing {   
    return thing;   
}   
- (void)setThing:(id)newThing {   
    thing = newThing;   
}   
// ...   
@end   

使用ARC,實(shí)例變量默認(rèn)是強(qiáng)類(lèi)型引用——將實(shí)例變量直接賦值給對(duì)象的確會(huì)擴(kuò)展對(duì)象的生命周期锌钮。遷移工具沒(méi)辦法決定實(shí)例變量何時(shí)應(yīng)該是 weak類(lèi)型的。為了像之前那樣維護(hù)同樣的行為引矩,你必須將實(shí)例變量標(biāo)記為 weak類(lèi)型梁丘,或使用一個(gè)聲明式屬性。

@interface MyClass : Superclass {   
    id __weak thing;   
}   
// ...   
@end   
@implementation MyClass   
- (id)thing {   
    return thing;   
}   
- (void)setThing:(id)newThing {   
    thing = newThing;   
}   
// ...   
@end   
或   
@interface MyClass : Superclass   
@property (weak) id thing;   
// ...   
@end   
@implementation MyClass   
@synthesize thing;   
// ...   
@end   

7.在C語(yǔ)言結(jié)構(gòu)體重不能使用強(qiáng)類(lèi)型的ids旺韭,例如氛谜,以下代碼會(huì)編譯不過(guò)
struct X { id x; float y; };
因?yàn)閤默認(rèn)肯定是被保留的,編譯器無(wú)法安全的合成所有保證代碼正常運(yùn)轉(zhuǎn)所需的所有代碼区端。例如值漫,如果你通過(guò)一些最終會(huì)被釋放的代碼來(lái)傳入一個(gè)指針到這些結(jié)構(gòu) 體后,每個(gè)id在結(jié)構(gòu)體回收之前會(huì)被被迫釋放掉织盼。編譯器不能可靠的做到這點(diǎn)杨何,所以在結(jié)構(gòu)體中的強(qiáng)類(lèi)型的ids在ARC模式下是完全無(wú)效的酱塔。以下有幾個(gè)可能 的解決辦法:
(1)使用Objective-C對(duì)象代替結(jié)構(gòu)體
這是最好的實(shí)踐方法。
(2)如果使用Objective-C對(duì)象是次優(yōu)方案危虱,(可能你想要這些結(jié)構(gòu)體組成的一個(gè)數(shù)組)那么考慮void *進(jìn)行替代羊娃。
這需要使用顯示的類(lèi)型轉(zhuǎn)換,在下面會(huì)提到埃跷。
(3)將對(duì)象引用標(biāo)記為_(kāi)_unsafe_unretained
該方法對(duì)于一些不常見(jiàn)的模式可能會(huì)有效蕊玷,如下:

struct x {NSString *S; int X;}  
StaticArray[]={  
    @"foo", 42,  
    @"bar",97,  
};  

這可能存在問(wèn)題,并且如果對(duì)象在指針之外會(huì)被釋放掉弥雹,這是不安全的垃帅,但實(shí)際對(duì)于像字符串常量一樣一直存在的東西來(lái)說(shuō)確實(shí)很有用的。
8.不能在 id與 void *(包括Core Foundation類(lèi)型)之間進(jìn)行直接轉(zhuǎn)換
在 “Managing Toll-Free Bridging.”中由詳細(xì)的描述剪勿。
【常見(jiàn)問(wèn)題】(Frequently Asked Questions)
我該怎樣理解ARC贸诚?它把retians/releases放在哪里呢?
嘗試不要再考慮retain/release應(yīng)該放在哪窗宦,要考慮你應(yīng)用的邏輯赦颇。考慮你對(duì)象中的”strong和weak“指針赴涵,對(duì)象的所有權(quán)媒怯,可能的循環(huán)引用。
我是否還需要為我的對(duì)象編寫(xiě)dealloc方法髓窜?
可能需要扇苞。
因?yàn)锳RC不會(huì)自動(dòng)操作malloc/free,Core Foundation對(duì)象的生命周期管理,文件描述寄纵,等等鳖敷。你仍需要在dealloc方法中是否這樣的資源。
你不需要(實(shí)際根本不需要)釋放實(shí)例變量程拭,但是你需要在系統(tǒng)類(lèi)和其它未使用ARC編譯的代碼上調(diào)用[self setDelegate:nil]定踱。
ARC中的dealloc方法不需要或不允許調(diào)用[super dealloc];到super的鏈?zhǔn)浇Y(jié)構(gòu)是在運(yùn)行期進(jìn)行處理和實(shí)施的。
在ARC中仍存在循環(huán)引用嗎恃鞋?
是的崖媚。
ARC自動(dòng)處理retain/release,并且繼承了循環(huán)引用的問(wèn)題。幸運(yùn)的是恤浪,遷移到ARC的代碼很少內(nèi)存泄露畅哑,因?yàn)閷傩裕╬roperty)已經(jīng)聲明了是否要保留。
在ARC下我能創(chuàng)建C數(shù)組的保留指針不水由?
是的荠呐,可以!

// Note calloc() to get zero-filled memory.   
__strong SomeClass **dynamicArray = (__strong SomeClass **)calloc(sizeof(SomeClass *), entries);   
for (int i = 0; i < entries; i++) {   
     dynamicArray[i] = [[SomeClass alloc] init];   
}   
// When you're done, set each entry to nil to tell ARC to release the object.   
for (int i = 0; i < entries; i++) {   
     dynamicArray[i] = nil;   
}   
free(dynamicArray);   

有些其它需要注意的地方:
某些情況下你需要使用__strong SomeClass **,因?yàn)槟J(rèn)是__autoreleasing SomeClass **.
分配的內(nèi)存必須是零填充的
你必須在釋放數(shù)組(memset或bzero無(wú)法工作)之前將每個(gè)元素設(shè)置為nil.
避免使用memcpy或realloc
ARC運(yùn)行速度會(huì)慢嗎泥张?
這取決于你的測(cè)量方式呵恢,但基本”很快“。編譯器高效的消除許多外部的 retian和 release調(diào)用圾结,而且總的來(lái)說(shuō)會(huì)更加關(guān)注于提升Objective-C運(yùn)行時(shí)的速度瑰剃。尤其是,當(dāng)調(diào)用者是ARC代碼時(shí)筝野,常用的"返回一個(gè) retain/autoreleased對(duì)象"模式會(huì)更快速而且實(shí)際上并沒(méi)有將對(duì)象放入到自動(dòng)釋放池中晌姚。
我可以在特定文件下退出ARC嗎?
可以歇竟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挥唠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子焕议,更是在濱河造成了極大的恐慌宝磨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盅安,死亡現(xiàn)場(chǎng)離奇詭異唤锉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)别瞭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)窿祥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蝙寨,你說(shuō)我怎么就攤上這事晒衩。” “怎么了墙歪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵听系,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我虹菲,道長(zhǎng)靠胜,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任毕源,我火速辦了婚禮髓帽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脑豹。我一直安慰自己,他們只是感情好衡查,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布瘩欺。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俱饿。 梳的紋絲不亂的頭發(fā)上歌粥,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音拍埠,去河邊找鬼失驶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛枣购,可吹牛的內(nèi)容都是我干的嬉探。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棉圈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼涩堤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起分瘾,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胎围,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后德召,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體白魂,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年上岗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了福荸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡液茎,死狀恐怖逞姿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捆等,我是刑警寧澤滞造,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站栋烤,受9級(jí)特大地震影響谒养,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜明郭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一买窟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧薯定,春花似錦始绍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)学赛。三九已至,卻和暖如春吞杭,著一層夾襖步出監(jiān)牢的瞬間盏浇,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工芽狗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绢掰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓童擎,卻偏偏與公主長(zhǎng)得像滴劲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子柔昼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容