什么是Automatic Reference Counting?
Automatic Reference Counting(ARC)是一個編譯器特性,它為Objective-C對象提供自動內存管理機制。相比手動地retain/release,ARC讓你專注于其它代碼,而不需要再考慮retain/release。下圖顯示了ARC和非ARC的區別。
[圖片上傳失敗...(image-f3472b-1574849633608)]
啟用 ARC之后,編譯器會自動在適當的地方插入適當的 retain, release,autorelease 語句。你不再需要擔心內存管理,因為編譯器為你處理了一切。
在使用toll-free bridging的時候,為了讓編譯器能插入正確的代碼,ARC做了一些限制。ARC還引進了新的生命周期修飾符。
ARC支持Xcode 4.2、OS X v10.6、iOS 4。但是weak指針在OS X v10.6和iOS4中不支持。
Xcode還集成了一個工具,可以把非ARC代碼轉化成ARC代碼(Edit > Refactor > Convert to Objective-C ARC)。這個工具可以把工程中所有的文件轉化成ARC代碼。你也可以手動指定某些文件使用ARC,而另一些文件不使用ARC。
更多知識:
Advanced Memory Management Programming Guide
Memory Management Programming Guide for Core Foundation
ARC 簡介
使用ARC,你不需要記住什么使用retain
、release
、autorelease
,ARC會自動處理對象的聲明周期,編譯的時候在合適的地方插入內存管理代碼。一般而言,只有當你需要和手動引用計數代碼交互的時候,傳統的Cocoa命名規則才是重要的。
一個完整正確的Person
類的定義可能看起來像這樣:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *yearOfBirth;
@property Person *spouse;
@end
@implementation Person
@end
(對象的屬性默認是strong,strong的含義會在下面講到)
使用ARC,你可以像這樣定義個一個contrived
方法:
- (void)contrived {
Person *aPerson = [[Person alloc] init];
[aPerson setFirstName:@"William"];
[aPerson setLastName:@"Dudney"];
[aPerson setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];
NSLog(@"aPerson: %@", aPerson);
}
ARC處理內存管理,所以Person
對象和NSNumber
對象都不會內存泄漏。
你還可以像這樣安全地給Person
類定義個一個takeLastNameFrom
方法:
- (void)takeLastNameFrom:(Person *)person {
NSString *oldLastname = [self lastName];
[self setLastName:[person lastName]];
NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}
ARC可以確保在NSLog
執行之前oldLastname
不會被釋放。
ARC使用新的規則
ARC引進了新的規則。這些規則旨在為ARC提供可靠的內存管理模型。在一些情況下,它們只是采取最好的做法。在另外一些情況下,它簡化你的代碼,或者明顯地告訴你你不需要處理內存管理。如果你違反了這些規則,你會得到一個編譯錯誤,而不是運行時錯誤。
- 你不能顯式地調用
dealloc
方法,也不能定義或者調用retain
、release
、retainCount
、autorelease
方法。
禁止使用@selector(retain)
、@selector(release)
等燈 - 你可能需要定義
dealloc
方法來管理資源。你不用(實際上你不能)release
實例變量,但是你可能需要在系統類或者沒有使用ARC編譯的代碼中調用[systemClassInstance setDelegate:nil]
。
自定義dealloc
方法中不需要調用[super dealloc]
(實際上會導致編譯錯誤)。父類方法的調用編譯器會自動幫你完成。
你仍然可以對CoreFoundation對象使用CFRetain
、CFRelease
或一些其它相關的方法。 - 你不能使用
NSAllocateObject
或者NSDeallocateObject
。
你用alloc
創建對象;runtime處理對象的釋放。 - 在C結構體中你不能只用對象指針。
你可以創建一個Objective-C類來代替結構體來處理數據。
id
類型和void
指針指間不能隨意轉換。
你必須指定轉換修飾符來告訴編譯器對象的生命周期。你需要在函數傳的參數中處理Objective-C和CoreFoundation*對象之間的轉換。 - 你不能使用
NSAutoreleasePool
對象。
ARC提供了@autoreleasepool
快來代替它。它比NSAutoreleasePool
更高效。 - 你不能使用內存
zones
。
沒有必要使用NSZone
了,它們會被modern Objective-C runtime忽略掉。
為了允許和手動retain-release代碼交互,ARC限制了方法命名:
你不能給屬性起一個以new開頭的名字。反過來說,除非你指定一個不同的getter
方法,否則你不能定義一個new開頭的屬性
// Won't work:
@property NSString *newTitle;
// Works:
@property (getter=theNewTitle) NSString *newTitle;
方法也是一樣,如果你在Person
類中寫了一個叫newPersonName
的方法,編譯器會假設這個方法返回一個新分配的對象。如果你的代碼只在ARC下,或者非ARC下運行,不會有什么問題。但是如果你使用ARC和非ARC混編,就會出現問題。如果定義這個方法的類用非ARC編譯,但是調用代碼用ARC編譯,你的程序會崩潰;相反,如果定義這個方法的類用ARC編譯,調用代碼用非ARC編譯,就會發生內存泄漏。
雖然你不能改變編譯器的行為,但是可以用clang宏NS_RETURNS_NOT_RETAINED
和NS_RETURNS_RETAINED
來改變方法的行為。你的newPersonName
可以像下面那樣申明,來讓它返回一個不被retain的對象。
- (NSString ) newPersonName NS_RETURNS_NOT_RETAINED;
但是不推薦這種方法,你可以為方法起一個不同的名字,比如- (NSString *) personName;
。但是,如果方法定義在靜態庫里,你沒法修改源碼,你可以用這兩個clang*宏。
ARC引進了新的生命周期限定符
ARC為對象和肉引用引進了一些新的生命周期限定符。weak指針不會擁有對象,并且當沒有其它strong指針指向這個對象的時候,weak指針會自動置為nil
。
你應該充分利用這些限定符來管理你的程序中的對象。特別提到一點,ARC不會
警告strong reference cycles(通常被叫做retain cycles)。恰當地使用weak關系可以幫你避免cycles。
Property屬性
屬性新增加了weak和strong關鍵字,如下面的例子所示。
// 下面的定義和這個是同義詞: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;
// 下面的定義和"@property(assign) MyClass *myObject;"類似
// 但是有一點不同,如果myObject指向的對象被釋放了,屬性會自動置為nil,而不是變成野指針。
@property(weak) MyClass *myObject;
在ARC下,默認的對象類型是strong。
變量限定符
你使用下面的生命周期限定符來修飾變量。
__strong
__weak
__unsafe_unretained
__autoreleasing
__strong
是默認的修飾符. 只要有一個strong指針指向對象,對象就保持存活。
__weak
指定個了一個不擁有對象的引用。一個weak引用在沒有其它strong引用指向對象的時候會自動置為nil。
__unsafe_unretained
指定了一個不擁有對象的引用,當沒有其它strong引用指向對象的時候,它不會被自動置為nil。當引用的對象釋放之后,這個指針就會變成野指針。
__autorelease
用來表示參數以引用傳遞,并返回autoreleased對象。
你應該正確地修飾變量。當對一個變量使用修飾符的時候,正確的格式是:
ClassName * qualifier variableName;
舉個例子:
MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
一些其它的變體在技術上講是錯誤的,但是可以被編譯器允許。想了解的話,查看。
在棧中使用__weak
變量的時候要注意。考慮下面的例子:
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);
盡管string在初始化賦值之后就用到了,但是在賦值執行的時候,沒有其它strong引用指向這個string;它馬上被釋放了。log語句顯式string的值是null。(編譯器在這種情況下會有一個警告)。
你還需要考慮對象以引用傳遞的情況。下面的代碼會工作:
NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
// Report the error.
// ...
然而,error
變量的定義是隱式地:
NSError * __strong e;
這個方法的定義個一般可能是這樣:
-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
編譯器會重新生成代碼:
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
// Report the error.
// ...
局部變量的定義(__strong
)和參數(__autoreleasing
)不符,導致編譯器創建了一個臨時變量。當你傳一個__strong
變量作為參數的時候,你可以把函數的參數類型定義個城id __strong *
,或者你可以把這個變量定義成__autoreleasing
。
使用生命周期限定符來避免Strong Reference Cycles
你可以使用生命周期限定符來避免strong reference cycles。例如,兩個對象互相 retain 時,會導致兩個對象都無法被釋放,這也是內存泄漏的常見原因之一。然后你可以把其中一個指針定義成weak,來打破循環。其它情況可能更微妙一些,特別是調用block對象的時候。
在手動引用計數模式下,
__block id x;
可以不retainingx
。在ARC模式下,__block id x;
默認會retainingx
(就像其它變量一樣)。為了在ARC中獲得手動引用計數一樣的效果,你可以使用__unsafe_unretained __block id x;
。像它的名字一樣,有一個non-retained對象是很危險的(因為它可能變成野指針),因此不鼓勵這么做。兩種不同的選擇是使用__weak
(如果你不需要支持iOS 4或OS X v10.6),或者把這個__block
值設置成nil來打斷retain cycle.下面的代碼片段使用手動引用計數有時使用的一個模式來說明這個問題。
MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
像上面講的那樣,你可以用一個__block
限定符,然后在completion handler
把myController
變量設置成nil:
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
或者你可以用__weak
臨時變量。下面的例子顯示了一個簡單的定義:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};
前面講到weak指針,沒有其它strong指針指向對象的時候會自動置為nil,為了保證block執行期間對象不被釋放(可能在其它線程中被釋放),你應該這么做:
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...
}
};
在某些類不兼容__weak
的情況下,你可以使用__unsafe_unretained
。但是,這樣做會讓你的循環不嚴格,因為很難或者不可能驗證__unsafe_unretained
指針是否有效,是否依然指向相同的對象。
ARC使用新的語句管理Autorelease Pools
使用ARC,你不能直接使用NSAutoreleasePool
類來管理自動釋放池。作為替代,你可以使用@ autoreleasepool
塊:
@autoreleasepool {
// Code, such as a loop that creates a large number of temporary objects.
}
這個簡單的結構允許編譯器思考引用計數狀態。進入塊的時候,一個自動釋放池被push進棧。在退出的時候(break
、return
、goto
、fall-through
等等)自動釋放池被pop。為了兼容現有的代碼,如果因為一個異常退出,自動釋放池不會被pop。
這個語法在所有Objective-C模式下都支持。它比NSAutoreleasePool
類效率更高;鼓勵用它來代替NSAutoreleasePool
的位置。
平臺間定義Outlets的模式變得一致
在iOS和OS X中使用ARC定義outlets的模式變得一致了。你應該遵循的模式是:outlets應該是weak,除了那些nib文件(或者storyboardscene)中頂層的對象應該是strong。
詳細的信息可以查看Resource Programming Guide中的Nib Files。
棧變量初始值為nil
使用ARC時,strong
、weak
和autoreleasing
棧變量隱式地用nil初始化。例如:
- (void)myMethod {
NSString *name;
NSLog(@"name: %@", name);
}
不會崩潰,而是打印name
的值為null
。
使用編譯Flags來啟用或關閉ARC
你用-fobjc-arc編譯標志來開啟ARC。如果大部分使用手動引用計數更方便的話,你可以選擇以文件為單位開啟ARC。對于那些開啟了ARC的工程,你可以用-fno-objc-arc編譯標志來給指定的文件關閉ARC。
ARC支持Xcode4.2以上,OS X v10.6以上,iOS4.0以上。Weak引用不支持OS X v10.6和iOS 4。Xcode 4.1和之前的版本不支持ARC。
處理Toll-Free Bridging
在許多Cocoa應用中,你需要用到Core Foundation風格的對象。不管你是直接只用Core Foundation框架(比如CFArrayRef
或CFMutableDictionaryRef
),還是使用集成了Core Foundation
框架的一些框架,比如Core Graphics框架(你可能用到CGColorSpaceRef
和CGGradientRef
)。
編譯器不會自動處理Core Foundation對象的生命周期;如Core Foundation內存管理規則(查看Memory Management Programming Guide for Core Foundation)里所述,你必須調用CFRetain
和CFRelease
(或者其它相關的變體)。
如果你在Objective-C和Core Foundation對象之間轉化,你必須要告訴編譯器怎么處理對象的生命周期,你可以使用類型轉換限定符(objc/runtime.h
里定義的)或者Core Foundation宏(NSObject.h
里定義的)。
__bridge
讓對象在Objective-C和Core Foundation之間轉換,但是并不轉換所有權。
__bridge_retained
或者CFBridgingRetain
把一個Objective-C指針轉化成Core Foundation指針,并把對象的所有權轉移給你。你負責調用CFRelease
或者相關的方法來釋放對象的所有權。
__bridge_transfer
或者CFBridgingRelease
把一個非Objective-C指針轉換到Objective-C指針,并把對象的所有權轉交給ARC。ARC 來負責對象的釋放。
例如,如果你有這樣的代碼:
- (void)logFirstNameOfPerson:(ABRecordRef)person {
NSString *name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSLog(@"Person's first name: %@", name);
[name release];
}
用可以用下面的代碼替換:
- (void)logFirstNameOfPerson:(ABRecordRef)person {
NSString *name = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSLog(@"Person's first name: %@", name);
}
編譯器處理從Cocoa方法返回的CF對象
編譯器理解遵循Cocoa命名規則(查看Advanced Memory Management Programming Guide),并返回Core Foundation類型的Objective-C方法返回。例如,編譯器知道,在iOS中,UIColor
類的CGColor
方法返回的CGColor
對象不會被擁有。但是你還是需要使用正確的類型轉換,如下面的例子所示:
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
使用所有權關鍵字轉換函數參數
當你在函數調用時在Objective-C和Core Foundation對象之間轉化,你需要告訴編譯器你傳的對象的所有權。Core Foundation對象的所有權規則和那些在Core Foundation內存管理規則中定義的規則一樣(查看Memory Management Programming Guide for Core Foundation);Objective-C對象的所有權規則在Advanced Memory Management Programming Guide中定義。
在下面的代碼段中,傳遞給CGGradientCreateWithColors
函數的數組需要一個合適的轉換。arrayWithObjects:
返回的對象的所有權沒有傳遞給函數,盡管用的是__bridge
。
NSArray *colors = <#An array of colors#>;
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
在下面的方法定義中,代碼段顯示在上下文中。注意(Core Foundation內存管理規則中規定的)Core Foundation內存管理函數怎么使用。
- (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.
}
#轉化工程到ARC時的常見問題
當遷移已有工程的時候,你可能會遇到許多問題。這里是一些常見的問題和解決方法。
你不能調用
retain
、release
或者autorelease
。
這是一個特性。你也不能這么寫:
while ([x retainCount]) { [x release]; }你不能調用
dealloc
。
典型地,如果你定義一個單例或者在init
方法中替換一個對象時,你要調用dealloc
。對于單例,使用共享實例模式。在init
方法中,你不需要再調用dealloc
方法,因為當你覆蓋self的時候對象會自動釋放。你不能使用
NSAutoreleasePool
對象。
使用心得@autoreleasepool{}
結構來代替它。它在你的自動釋放池上包裝了一個塊結構,并且會比NSAutoreleasePool
快上6倍。@autoreleasepool
在非ARC代碼下也能工作。因為@autoreleasepool
比NSAutoreleasePool
快很多,所有許多“性能問題”可以無腦地把NSAutoreleasePool
替換為@autoreleasepool
。
遷移工具可以處理簡單的NSAutoreleasePool
,但是不能處理復雜情況下的,也不能處理在@autoreleasepool
塊里定義變量,然后在后面才使用這個變量的情況。** ARC需要你在
init
方法中把[super init]
的返回值賦給self
。
下面的init
方法在ARC**中是非法的:
[super init];
簡單的修改一下:
self = [super init];
完整的修改方法是像上面那樣做,并在繼續前檢查結果是nil:
self = [super init];
if (self) {
...你不能自定義
retain
或者release
方法。
自定義retain
或者release
方法來實現weak
指針。你可能因為一些常見的原因想要自定義:性能.
請不要在這么做;現在NSObject
類的retain
和release
方法比以前更快了。如果你還是發現問題,請提交bug。定義weak指針系統
使用__weak
來代替。定義單例。
使用共享實例模式來代替。或者,使用類方法代替實例方法,可以完全避免分配對象。-
"Assigned"實例變量變成
strong
。
在ARC之前,實例變量不擁有對象-直接給實例變量賦值一個對象不會保持對象的生命周期。為了使屬性變成strong
,你通常可以定義或者synthesized
存取器方法來調用合適的內存管理方法;形成鮮明地對比,你可能像下面例子中展示的那樣,定義了一個存取器方法來獲得一個weak屬性。
@interface MyClass : Superclass {
id thing; // Weak reference.
}
// ...
@end@implementation MyClass - (id)thing { return thing; } - (void)setThing:(id)newThing { thing = newThing; } // ... @end
使用ARC,實例變量默認是strong
引用-直接給實例變量賦值一個對象會保持對象的生命周期。遷移工具不能決定什么時候實例變量是weak
。為了得到之前一樣的效果,你必須把實例變量標志為weak,或者使用屬性。
@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
- 你不能在C結構題中使用
strong
對象。
例如下面的代碼不能通過編譯:
struct X { id x; float y; };
這是因為x默認是strong
,編譯器不能安全地合成讓它能正確工作的代碼。例如,如果你傳了一個指向那種結構體的指針到處理釋放的代碼中,每個id
都會在結構體釋放前釋放。編譯器做這些是不可靠的,所以在ARC模式下結構題中strong
對象是不允許的。有一些解決方案:- 使用Objective-C來代替機構提。
這被認為是最好的方案。
- 使用Objective-C來代替機構提。
- 如果使用Objective-C是次級方法,(可能你想使用這些結構體的數組)你可以考慮使用
void *
來代替。
這需要使用顯示的轉換,如下面所述。 - 把對象引用弄成
__unsafe_unretained
。
這個方法在像這樣的并不常見的模式中可能有用:
struct x { NSString S; int X; } StaticArray[] = {
@"foo", 42,
@"bar, 97,
...
};
你像這樣定義結構題:
struct x { NSString * __unsafe_unretained S; int X; }
這樣做可能會出問題,并且是不安全的,因為對象可能會被釋放,但是對于那些永久存在的對象可能非常有用,比如字符串常量。
你不能在id
類型和void*
類型之間直接轉換(包括Core Foundation類型)。
這些在前面的處理Toll-Free Bridging*中已經討論過。
常見問題
我怎么理解ARC?它在哪里插入retain/release?
不要去考慮retain/release在哪里插入的,去考慮你的程序的算法。考慮你的對象的"strong and weak"指針,考慮對象的擁有關系,考慮哪里可能會retain cycles。
我仍然需要給我的對象寫dealloc
方法嗎?
可能需要。
因為ARC不會自動malloc/free
,不會管理Core Foundation對象和file descriptors的生命周期,等等。你還是需要寫一個dealloc
方法來釋放這些資源。
你不需要去釋放實例變量,但是你可能需要給系統類和其它沒有使用ARC編譯的代碼調用[self setDelegate:nil]
。
ARC仍然可能發生retain cycles嗎?
是的。
ARC自動retain/release,并繼承了retain cycles問題。幸運的是,遷移到ARC的代碼很少發生內存泄漏,因為不管指針的屬性是不是retain,都已經自動合成了釋放代碼
block在ARC里怎么工作的?
在ARC模式下,你僅僅需要在棧上傳block,它們就能工作了。你不再需要調用Block Copy。
有一點需要注意,NSString * __block myString
在ARC模式下會retain對象,而不是一個可能會變成野指針的指針。為了獲得以前一樣的效果,使用__block NSString * __unsafe_unretained
或使用__block NSString * __weak myString
(這種更好)。
我可以在OS X雪豹上用ARC開發嗎?
不行。雪豹Xcode4.2不支持ARC,因為它不包含10.7SDK。雪豹Xcode 4.2支持iOS上的ARC,LionXcode 4.2OS X和iOS都支持。這意味著,你需要在Lion系統上buildARC應用,然后在雪豹系統上運行。
在ARC下我可以創建一個retained指針的C數組嗎?
是的,你可以,下面例子里說明了怎么做:
// Note calloc() to get zero-filled memory.
__strong SomeClass **dynamicArray = (__strong SomeClass **)calloc(entries, sizeof(SomeClass *));
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 **
,因為默認是__autoreleasing SomeClass **
。
這些分配的內存必須是零填充。
在釋放數組(memset
或者bzero
沒用)前必須要把每個元素都設置成nil。
你應該避免memcpy
或者realloc
。
ARC慢嗎?
它取決于你測量什么,但是一般情況下是“不會”。編譯器有效地消除了許多retain/release調用,而且通常Objective-C runtime為ARC的速度作了很多優化。特別是,那些返回一個retain/autoreleased對象的模式會更快,因為當調用方法的代碼是ARC模式下,對象不會被放進自動釋放池。
一個需要注意的問題是在通常的debug配置下優化程序不會運行,因此在-O0下比-Os下可以看到更多retain/release。
ARC在ObjC++模式下能工作嗎?
是的。你也可以把strong/weak對象放在類和容器里面。ARC編譯器在拷貝構造函數和析構函數中自動生成retain/release邏輯。
哪些類不支持weak引用?
目前你不能創建下面類的weak引用:
NSATSTypesetter, NSColorSpace, NSFont, NSMenuView, NSParagraphStyle, NSSimpleHorizontalTypesetter, and NSTextView.
注意:另外,在OS X v10.7下,你不能創建NSFontManager, NSFontPanel, NSImage, NSTableCellView, NSViewController, NSWindow, 和 NSWindowController的weak引用。還有,在OS X v10.7下AVFoundation框架下的類都不支持weak引用。
定義屬性的時候,你應該用assign
來代替weak
;對于變量,你應該使用__unsafe_unretained
代替__weak
。
另外,在ARC下你不能創建NSHashTable,NSMapTable,或NSPointerArray的weak引用。
當子類化NSCell或其它類時使用NSCopyObject
我需要怎么做?
沒有特別需要注意的。ARC代替你來處理以前需要額外顯示地retain的地方。在ARC下,所有的copy方法應該只在實例變量上copy。
我能指定部分文件用ARC編譯嗎?
可以。
當你遷移一個工程到ARC,默認所有的Objective-C文件都被設置了-fobjc-arc編譯標志。你可以用* -fno-objc-arc來為部分類禁用ARC。在Xcode里,在target的Build Phases標簽,打開Compile Sources組來
瀏覽源文件列表。雙擊你想設置的文件,在彈出的框中輸入-fno-objc-arc
,然后點擊Done*。
[圖片上傳失敗...(image-699fca-1574849633608)]
GC(垃圾回收)在Mac被棄用了嗎?
垃圾回收在OS X Mountain Lion v10.8中被棄用了,并且將會在將來的OS X版本中被刪除。自動引用計數是推薦的替換技術。為了幫助遷移已有的應用,Xcode 4.3和之后的ARC遷移工具支持把OS X程序中的垃圾回收遷移到ARC。
注意:對于Mac App Store上的應用,蘋果強烈推薦盡可能地用ARC替換垃圾回收機制,因為Mac App Store指南(查看App Store Review Guidelines for Mac Apps)禁止使用棄用的技術。