什么是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)禁止使用弃用的技术。