谁能简单地给我解释一下ARC是如何工作的?我知道它不同于垃圾收集,但我只是想知道它究竟是如何工作的。
而且,如果ARC在不影响性能的情况下完成了GC的工作,那么Java为什么要使用GC呢?为什么不使用ARC呢?
它与垃圾收集有很大的不同。您是否看到警告,告诉您可能在不同的行上泄漏对象?这些语句甚至告诉你在第几行分配了对象。这已经进一步,现在可以插入retain/release语句在适当的位置,比大多数程序员更好,几乎100%的时间。偶尔会有一些奇怪的保留对象的实例,您需要帮助它。
retain
release
魔法
但更具体地说,ARC的工作原理与你的代码完全一样(有一些细微的区别)。ARC是一种编译时技术,不像GC是运行时技术,它会对你的性能产生负面影响。ARC将为您跟踪对象的引用,并根据正常规则综合保留/释放/自动释放方法。因此,ARC也可以在不再需要的时候立即释放它们,而不是纯粹为了约定而将它们扔到自动释放池中。
其他一些改进包括弱引用归零、自动将块复制到堆、全面加速(自动释放池的速度为6倍!)
关于所有这些如何工作的更详细的讨论可以在ARC上的LLVM文档中找到。
每个接触Objective-C的新开发人员都必须学习何时保留、释放和自动释放对象的严格规则。这些规则甚至指定了暗示从方法返回的对象的保留计数的命名约定。一旦你把这些规则牢记于心并始终应用它们,Objective-C中的内存管理就会成为你的第二天性,但即使是最有经验的Cocoa开发人员也会时不时地犯错。
使用Clang Static Analyzer, LLVM开发人员意识到这些规则足够可靠,他们可以构建一个工具来指出代码所采用的路径中的内存泄漏和过度释放。
自动引用计数 (ARC)是下一个逻辑步骤。如果编译器能够识别应该在哪里保留和释放对象,为什么不让它为您插入代码呢?严格的、重复的任务是编译器及其同类所擅长的。人类会忘记事情,会犯错误,但计算机的一致性要高得多。
然而,这并不能让您完全摆脱对这些平台上内存管理的担忧。我在我的回答在这里中描述了要注意的主要问题(保留循环),这可能需要你花一点心思来标记弱指针。然而,与您在ARC中获得的相比,这是次要的。
与手动内存管理和垃圾收集相比,ARC提供了两方面的优点,既不需要编写保留/释放代码,又没有垃圾收集环境中出现的暂停和锯齿状内存配置文件。与此相比,垃圾收集的唯一优点是它能够处理保留周期,并且原子属性赋值是廉价的(如前所述在这里)。我知道我正在用ARC实现替换所有现有的Mac GC代码。
至于这是否可以扩展到其他语言,它似乎与Objective-C中的引用计数系统有关。将其应用于Java或其他语言可能很困难,但我对底层编译器的细节了解不够多,无法对此做出明确的说明。考虑到Apple是LLVM中推动这项工作的一方,Objective-C将首先出现,除非另一方将自己的大量资源投入其中。
这一消息的公布震惊了全球开发者大会上的开发者,所以人们没有意识到这样的事情是可以做到的。随着时间的推移,它可能会出现在其他平台上,但目前它只适用于LLVM和Objective-C。
ARC只是使用旧的保留/释放(MRC),由编译器确定何时调用保留/释放。与GC系统相比,它具有更高的性能、更低的峰值内存使用和更可预测的性能。
另一方面,ARC(或MRC)无法处理某些类型的数据结构,而GC可以处理它们。
举个例子,如果你有一个名为node的类,node有一个NSArray的子类,以及一个对它的父类的引用,这个引用“只适用于”GC。对于ARC(以及手动引用计数),您有一个问题。任何给定的节点都将被它的子节点和父节点引用。
如:
A -> [B1, B2, B3] B1 -> A, B2 -> A, B3 -> A
当您使用A时(例如通过局部变量),一切都很好。
当你完成它(和B1/B2/B3)时,GC系统将最终决定从堆栈和CPU寄存器开始查看它能找到的所有东西。它永远不会找到A,B1,B2,B3,所以它将最终确定它们,并将内存回收到其他对象中。
当你使用ARC或MRC,并完成与A它有一个引用计数3 (B1, B2和B3都引用它),B1/B2/B3都将有一个引用计数1 (A的NSArray持有一个引用)。所以所有这些对象都是活的,即使没有任何东西可以使用它们。
常见的解决方案是确定其中一个引用需要是弱引用(不影响引用计数)。这在某些使用模式下是可行的,例如,如果你只通过a引用B1/B2/B3,但在其他模式下它就失败了。例如,如果你有时会抓住B1,并期望通过父指针爬回来找到a。如果你只抓住B1, a可以(通常会)蒸发,带走B2和B3。
有时这不是一个问题,但是一些非常有用和自然的处理复杂数据结构的方法在ARC/MRC中很难使用。
ARC的目标和GC的目标是一样的。然而,ARC的使用模式比GC更有限,所以如果您使用GC语言(如Java)并将ARC之类的东西嫁接到它上面,那么一些程序将不再工作(或者至少会产生大量的废弃内存,并可能导致严重的交换问题或耗尽内存或交换空间)。
您也可以说,ARC更注重性能(或可预测性),而GC更注重作为通用解决方案。因此,GC的CPU/内存需求较少,性能(通常)比ARC低,但可以处理任何使用模式。对于许多常见的使用模式,ARC会工作得更好,但对于少数(有效的!)使用模式,它会崩溃并死亡。
苹果开发者文档解释得很好。读“ARC如何运作”
为了确保实例在仍然需要时不会消失,ARC会跟踪当前每个类实例引用了多少属性、常数和变量。只要至少有一个对该实例的活动引用存在,ARC就不会释放该实例。 为了确保实例在仍然需要时不会消失,ARC会跟踪当前每个类实例引用了多少属性、常数和变量。只要至少有一个对该实例的活动引用存在,ARC就不会释放该实例。
为了确保实例在仍然需要时不会消失,ARC会跟踪当前每个类实例引用了多少属性、常数和变量。只要至少有一个对该实例的活动引用存在,ARC就不会释放该实例。
要了解垃圾收集和ARC之间的差别:请阅读这
ARC是一个编译器特性,提供对象的自动内存管理。
你不必记住什么时候使用retain, release和autorelease, ARC会评估对象的生存期需求,并在编译时自动为你插入适当的内存管理调用。编译器还为您生成适当的dealloc方法。
retain, release
autorelease
编译器在编译时插入必要的retain/release调用,但这些调用在运行时执行,就像任何其他代码一样。
retain/release
下面的图表将使您更好地理解ARC的工作原理。