1,由于有限的内存以及手持设备续航能力的限制,iOS应用程序中的Objective-C对象的管理一直颇有挑战性。为了处理这些问题,苹果提出了一个方案——“自动引用计数”(ARC)。在这篇文章中,我会把ARC和显式的retain/release以及垃圾回收进行对比,除此之外还会展示如何在一个iOS项目中使用它,并且探讨一些ARC的使用准则。
2,首先,我通过显示的消息传送来管理ObjC对象。我用alloc和init消息来创建对象(如图 1)。我发送retain消息来保持一个对象,并且发送release消息来释放掉它。通过alloc/init创建的ObjC对象会有一个内部的值为1的引用计数。retain消息会使这个引用计数加1,然而,release消息会使这个引用计数减1.当这个引用计数为0时,这个对象会自动销毁,释放它所持有的内存。
3,我们也可以用一个工厂方法来创建ObjC对象。这样就标记了这个对象是自动释放的,将他的指针加到自动释放池(如图 2)。我们可以通过autorelease消息来实现对alloc/init的对象达到发送release消息的目的。
4,在每一个事件周期中,自动释放内存池都会去检测自身的对象指针集。当它发现超出其作用域并且引用记数为1的对象,它就会通过发送一个release消息释放这个对象。当不想释放这个对象时,我们可以发送一个或多个retain消息给这个对象。否则,我们必须让这个对象发送的retain和release消息一样多,才能将它释放。
显式发送消息的方式仍然是iOS应用程序中管理ObjC对象的一种有效方法。它一般不会花费很多精力,可以很容易的定位bug,同时拥有性能好的特点。
在另一方面,显式发送消息的方式很容易导致出错。当retain和release消息不相等时,它会导致内存泄露或EXC_BAD_ACCESS错误。另外,显式地释放一个已经释放了的对象也会导致EXC_BAD_ACCESS错误。并且,对象容器(如数组,集合等)可能并不会对它包含的引用记数大于1的对象运行该对象的构造函数。
5,MacOS X 10.5 (Leopard) 给我们另一个管理ObjC对象的方法— 垃圾回收。这里,每一个Cocoa应用程序得到自己的作为次级线程运行的收集服务,这个服务标识所有在一起动就创建的根对象,然后跟踪每一个后来创建的对象。它检查每一个对象的范围以及对根对象的强引用。如果对象有这些特性,那么收集服务将它保留下来(用蓝色标记)。否则,它使用一个finalize消息释放这个对象(用红色标记)。
收集服务是保守的。当必须保证高性能时,它可以被中断,甚至暂停 。它是一个分代的服务。他假定最新被创建的对象寿命最短。
通过类NSGarbageCollector来使用收集服务。使用这个类,我能够禁用这个服务或者改变它的行为。我甚至能指定新的根对象或者重置服务本身。
垃圾回收移除了显式的保留和释放消息的需要。它能够降低野指针也能够防止空指针。换句话说,它需要所有定制的ObjC对象被更新。清除代码必须进入到finalize方法,而不是 dealloc方法。ObjC对象也必须向他的父发送一个finalize消息。
接下来,收集服务需要知道何时一个对象的引用是弱的。否则,他假设所有的引用都是强的。这可能导致循环引用和内存泄漏。这个服务也忽略使用withmalloc()创建的对象:那些对象应该被手动释放或者使用Cocoa函数NSAllocateCollectable()来创建。
最后,这个服务依然会导致性能受到影响,尽管他是保守的。这就是垃圾回收在iOS上缺席的原因。
6,
ARC是一种全新的方式,它拥有很多垃圾回收机制的优点,但却没有那样的性能损耗。
从内部来看,ARC并不是一项运行时的服务。实际上它是由新的Clang front-end提供的两段过程。图4显示了这两段过程。在front-end段时,Clang检查每个预处理文件的对象和属性。然后它跟据一些固定的规则将正确的retain,release和autorelease语句加入其中。
7,如果对象被分配内存并处于一个方法当中,它会在这个方法的结尾处获得一个release语句。如果是一个类属性,它的release语句会加入到类的dealloc方法中。如果这个对象是用来返回的或者它是一个容器对象,它会加入一个autorelease语句。又如果这个对象是弱引用,把它放在一边不管它。
8,前端也为非局部对象插入保留语句。他更新所有使用@property指示符声明的访问器。他添加了对父的dealloc的调用,并且他报告任何的显式的管理调用和任何不清晰的所有权在优化阶段,Clang 使修改过的代码遵从加载平衡。它统计每一个对象保留和释放的调用,然后把它们缩减到优化的最小值。这避免了过度的保留和释放,能够在性能上产生影响
9,
因为ARC独自决定ObjC对象如何被管理,它省掉了开发类代码的时间。它阻止了任何野指针和空指针。它甚至能够基于文件来禁用。最后一个特征让程序员可以复用被证明是稳定的遗留代码。
但是Clang编译器被构建在LLVM 3.0中,它只能在Xcode 4.2或者更新的版本中获得.对于ARC的优化的运行时支持也仅仅出现在MacOS X 10.7 (Lion) 和 iOS 5.0。在iOS 4.3中通过粘合代码使用ARC是可能的。在后来提供的不使用任何弱指针的二进制文件中使用ARC也是可能的。
然后,ARC只对ObjC代码起作用。对于PyObjC和AppleScriptObjC代码没有任何效果。但是,它的确影响那些桥接PyObjC,ASOC类到Cocoa的底层ObjC对象。也值得注意的是,一些第三方的框架在使用ARC开启的编译的时候也可能会导致问题 。请确保在更新版本时联系框架的开发者。