http://transideology.com/2015/09/%E7%BF%BB%E8%AF%91transitioning-to-arc-release-notes/
模式下运行。鉴于 @autoreleasepool
比 NSAutoreleasePool
快如此之多,一些老的性能优化小技巧可以被简单的替换成 @autoreleasepool
方式了。
转换器可以处理一些简单的 NSAutoreleasePool
使用,对于一些复杂的情况,或者定义在新的@autoreleasepool
然后被后面代码使用的变量,转换器都无法自动处理。
- ARC要求你在
init
方法中调用[super init]
,并将返回值赋给self
下面例子使用 init
在ARC模式下是不正确的
[super init];
简单的修复一下
self = [super init];
合适的方法是
self = [super init];
if (self) {
...
- 你不能实现自定义
retain
和release
方法
实现自定义 retain
和 release
方法会破坏weak指针,下面是一些你希望提供自定义retain
和 release
方法的常见原因(译者注:这里指的是过去MRC,并不意味着在ARC时代你可以自定义这两个方法)。
1、性能原因
请不要再这么做的,因为 NSObject
对象的 retain
和 release
方法更加快了。如果你还是发现有问题,请提交bug给我们处理。
2、实现自定义的weak指针
请直接使用 __weak
3、实现一个单例
请使用共享的实例设计模式。除此之外,请使用类方法而不是实例方法,这样可以避免分配对象。(译者注:我理解共享实例设计模式是,单例的最终实例对象是被一个全局的static指针指向的,当每次获取单例的对象时,使用类的方法调用,而不是实例的方法,这样避免了每次对象分配,只需要第一次在类方法中多线程安全的alloc一次。)
- 被赋值(assigned)实例变量是strong引用的
在ARC之前,实例变量是不拥有对对象的引用的——直接把一个对象赋值给实例变量是不能增加对象的生命周期的(译者注:即不能增加对象的引用基数,因为在MRC时代需要主动调用retain,或者根据命名规范,一些new开头、alloc等方法会增加引用计数)。为了产生一个strong属性(译者注:这里仍然指的是MRC时期),你需要实现或生成存取器方法(accessor methods),在这些方法内部去调用合适的内存管理方法。而对于weak属性,你需要像下面的例子所展示的那样去实现存取器方法来管理weak属性。(译者注:下面代码是MRC的,我理解MRC没有weak属性的,只是默认需要手动retain才会增加对象的引用计数,所有不调用retain就相当于一个weak引用,但并没有ARC下weak指针自动nil的功能,这里等价于ARC中的__unsafe_unretained)
@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引用的实例变量主动标记为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
id
s
如下代码无法通过编译
struct X { id x; float y; };
因为 x
默认是strong引用,编译器无法安全可靠的生成让它正确工作所需要的所有代码。例如,你将一个指向结构体的指针传递到一段在结尾处会 free
这个结构体的代码,每一个 id
都必须在结构体 free
前被释放。编译器无法安全的生成这些额外的代码,所以在ARC模式下strong id是不允许的(译者注:不理解为何不能生成安全的代码,后面提到在c++类中可以使用)。以下是一些可行的解决方案
1、使用Objective-C对象来代替结构体
这个是最好的方案
2、如果不方便使用Objective-C(或许你想使用存放结构体的原生数组),可以考虑用 void*
代替在结构体中定义id
这种方法要求显式转换(译者注:即使用Toll-Free bridging 中的三种方法)
3、将对象的引用关系标记为 __unsafe_unretained
这种方法在一些不怎么更改带有定义常量性质的结构体中很有用,
struct x { NSString *S; int X; } StaticArray[] = {
@"foo", 42,
@"bar, 97,
...
};
你可以这样定义结构体
struct x { NSString * __unsafe_unretained S; int X;
如果NString指针的对象被释放的话,用这种方法可能不安全,但是如果你确定这些结构体是用来像常量一样来保存一些不变的东西的话,这么做还是很有用的。
- 你不能直接在
id
和void*
之间做转换(包括Core Foundation 类型)(译者注:这里直接指的是隐式转换)
详细内容可以参考 Managing Toll-Free Bridging.(译者注:可以使用bridge的三种方法转换。)
经常被问到的一些问题
我应该怎么看待ARC?它在哪里增加了 retains/release?
尝试不要再去思考 retain/release 被自动添加到哪里了,多去思考一下你程序的算法吧。思考你的对象的strong和weak指针以及对象之间的关系,避免retain cycle的问题。
我是否还需要为对象写 dealloc 方法
或许需要
因为ARC并不自动执行 malloc
/free
操作,也不管理Core Foundation对象、文件和其他一些对象的生命周期,所以你仍然需要写一个 dealloc
来去释放这些资源。
你不应该也不能够去释放一个实例变量,但是你可能需要在系统类上调用 [self setDelegate:nil]。
在ARC模式下,dealloc
中不允许调用[super dealloc],运行时库会负责维护这条继承链的调用的。
ARC中是否还存在 retain cycles
存在
ARC其实是自动调用 retain/release,所以同样的问题仍然存在。幸运的是,ARC代码中很少出现泄漏问题,因为属性已经声明了是否需要被retained。(译者注:即可以在适当的地方声明为weak引用)
ARC中的block是如何工作的
Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more.(译者注:由于对block理解不深,这里不做翻译,大家自己理解吧。待以后详细研究了block再做翻译。)
不过你需要注意的一件事情是,在ARC下使用 NSString * __block myString
时,myString
是被retained,如果想要之前的效果(译者注:在MRC使用__block是,除了表明变量在block中可以修改,也表明变量不会被自动retained,即block对象不会增加变量的引用计数),请使用__block NSString * __unsafe_unretained myString
或者最好使用 __block NSString * __weak myString
.。
我可以在Snow Leopard版本的系统上使用ARC吗?
不可以,因为Snow Leopard系统上的Xcode4.2不支持ARC,这个系统没有包含10.7的SDK。Snow Leopard上的Xcode4.2也不支持iOS,Lion系统的Xcode4.2支持iOS和OS X。这意味这你需要Lion系统来编译ARC代码。
在ARC模式下,我可以创建一个C Array,里面retain 对象的引用吗?
可以的,代码如下
// 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 **
,因为如果不使用strong,ARC默认生成的是__autoreleasing SomeClass **
。(见上文提交的NSError的代码) - 分配的内存填充的必须是0
- 释放array前你必须把array中的每个元素都赋值为
nil
(memset
或bzero
是无效的)。 - 你应该避免使用
memcpy
orrealloc
。
ARC慢吗?
这样看你指的哪方面,通常情况下答案是否定的。编译器会高效的删除许多没有必要的retain
/release
,我们也做了很多努力来提升Objective-C运行时库的速度。尤其是,常用的“返回 retain/autoreleased 对象”的模式在ARC下是非常的快的,当调用这样的方法的时候,并不真的把对象放在自动释放池里。(译者注:具体怎么做求大神指点)
需要注意的一个问题是,在debug模式下优化是不生效的,所有想要看到一些ARC的技术细节应该使用编译选项-O0
而不是-Os
。
ARC在ObjC++模式下生效吗?
是生效的。你甚至可以在类和容器中使用strong/weak id
s 。ARC编译器会在拷贝构造函数和析构函数中生成retain
/release
调用的代码。
那些类不支持weak引用?
目前NSATSTypesetter
, NSColorSpace
, NSFont
, NSMenuView
, NSParagraphStyle
,NSSimpleHorizontalTypesetter
, and NSTextView
.这些类不能被创建使用weak指针来引用的实例。
注意:除此之外,在OS X10.7上不能创建NSFontManager
, NSFontPanel
, NSImage
,NSTableCellView
,NSViewController
, NSWindow
, and NSWindowController
的弱引用实例。OS X10.7上AV Foundation中的类都不能用弱引用。
对于这些类,如果是当作属性使用的话,可以使用来assign
代替weak
。如果是定义为变量的话可以使用__unsafe_unretained
。除此之外NSHashTable
, NSMapTable
, orNSPointerArray
也不能用弱引用。
如果我要子类化NSCell或者子类化其他使用NSCopyObject的类,我应该怎么做?
请正常使用,没有什么特别的地方。对于你之前要显示调用retain的地方,ARC会负责处理的。在ARC模式下,所有的copy方法都应该仅仅是浅拷贝。(这里原文是all copy methods should just copy over the instance variables. 翻译的是否正确有待第二次校验。)
我是否可以指定一些文件不使用ARC
可以的。
当你把一个工程转换成ARC时,所有的Objective-C原文件都默认使用-fobjc-arc
编译选项。你可以使用-fno-objc-arc
编译选项来对一些类关闭ARC。(译者注:这里去掉了设置步骤,因为文章是2013年的,目前2015的Xcode7已经和当时差异很大了。我不会告诉你我是翻译累了不想写了。)
在Mac上GC(Garbage Collection)被弃用了吗?
在OS X Mountain Lion v10.8上GC已经被弃用了,在以后的版本中也不再会使用。ARC是推荐的技术,为了转化已有的工程,Xcode 4.3和以后版本中的转化工具会帮助Mac程序从GC迁移到ARC模式的。
注意:对于Mac程序,Apple强烈建议尽快从GC转化为ARC方式,因为Mac应用市场规范禁止使用废弃的技术。