自動(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ù)叫潦。
- 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嗎?
可以歇竟。