在Objective-C中同时使用垃圾回收器和自动引用计数(ARC)

4

我了解到,在Objective-C中,垃圾回收是使用AUTOZONE(LIBAUTO)完成的。

同时,垃圾回收器在OS X 10.8之前可用。在学习时,我想知道如果垃圾回收器可用,ARC的必要性是什么。然后从stackoverflow.com的资源中,我了解到了ARC和垃圾回收器之间的区别以及它们相互之间的优势。

现在我知道ARC在编译时工作,而垃圾回收器在运行时工作。此外,ARC无法释放CFTypes的内存,它只适用于Objective-C类型。而垃圾回收器可以释放由ARC无法释放的保留循环。

现在我想知道是否可以同时使用ARC和垃圾回收器,因为两者在OS X 10.7中都可用。另外,为什么在10.8之后垃圾回收器被弃用了。ARC是否是垃圾回收器的替代品,它能够释放垃圾回收器所能释放的每种对象。

此外,由于iOS中没有垃圾回收器,那么在ARC出现之前,iOS使用了什么进行垃圾回收。如果手动内存管理得当,是否能够管理所有内容,而不需要垃圾回收。


2
ARC旨在成为垃圾收集器的卓越替代品。我不知道有任何官方方法同时使用它们两个,也看不出您需要这样做的任何理由。 - Greg
1
我不使用ARC。在ARC出现之前,我已经开始在Objective-C++中使用C++的智能指针。它比苹果的ARC更好:它可以完美地与旧的非ARC代码配合使用,并且不需要代码转换,在您的情况下可以与垃圾回收一起使用。因此,我的建议是:如果您熟悉C++,那么最好使用智能指针而不是ARC。 - Cy-4AH
4
ARC 用于 Objective-C 的内存管理,而智能指针(例如 std::shared_ptr)用于 C++ 的内存管理。你不能把它们视作互相替代的,因为它们有不同的角色。 - jbat100
谢谢你们两个。我目前没有在使用这个项目工作。只是在澄清我对Objective C内存管理的概念。 - Neha
1
@Cy-4AH:此外,试图重新发明像std::shared_ptr这样的东西是某些事情出了问题的明确迹象:你真的认为你一个人可以比一群有多年经验的其他专家做得更好吗? - DarkDust
显示剩余3条评论
4个回答

4
据我所知,没有(简单的)方式可以同时启用两者,而且这也没有意义。您已经几乎回答了这个问题:GC和ARC都是为了清理内存而服务的,GC有捕捉ARC无法捕捉的优势,但付出的代价是运行时延迟。使用ARC,如果使用不正确仍然可能泄露内存,但如果做得正确(主要是遵守命名约定),ARC具有卓越的运行时性能,使您从手动管理负担中解脱出来。因此,您选择其中之一,混合使用并不是很有意义。如果启用了GC,则ARC正在执行的所有工作几乎都是没有用的,这将浪费性能。顺便说一下,在ARC之前,我们使用retain/release进行手动内存管理。事实上,这仍然是一个选项,并且广泛使用(例如,我在一项巨大的项目上工作,将其转换为ARC会很麻烦)。

@DarkDust “一个庞大的项目,转换为ARC会很麻烦” - 顺便问一下,你尝试过XCode的“转换为ARC”工具吗?或者这对你不起作用? - TheDarkKnight
@Merlin069:对于这个特定的项目来说,情况并不那么容易,因为我们在一些领域使用了一些ARC禁止的技巧,移植它们需要一些工作。由于我们都是来自于ARC之前的开发者,所以这并不是什么大问题。Instruments告诉我们没有泄漏,那么为什么要改变一个运行良好的系统呢? :-) - DarkDust
@DarkDust,谢谢你的解释;我一直怀疑苹果代码是否能够创建这样一个工具来满足每个项目的需求;O) - TheDarkKnight

3
在C语言中,内存管理通常使用malloc和free函数来实现。在C++中,首选的方法是new和delete。这两种方法都依赖于开发人员准确地知道他们已分配了哪些内存,以及它们所在的位置(堆栈还是堆),并且必须仅释放该内存一次!
如果两个对象引用同一个其他对象,那么哪个对象负责清理该对象会变得很麻烦。
Objective-C通过使用retain/release解决了这个问题,它管理对象的内部引用计数。当计数为零时,对象被删除并释放其内存。开发人员通常手动实现此方法,而对于C和C ++,也可以自动使用智能指针。
从Objective-C的发展来看,苹果不断努力使语言更简单易用,并减少常见错误的风险,通过自动化样板代码。
因此,从手动处理retain和release到GC是下一步,但如上所述,运行时存在GC运行的开销。 GC二进制文件与引用计数(手动和自动)完全不同且不兼容。这就是为什么在开发系统偏好设置包时,仍然必须启用GC,因为主机应用程序(系统偏好设置)即使在Mavericks中仍然使用GC。忘记使用GC编译第三方首选项包将导致包的加载失败。
ARC的开发回归到retain / release模型,但编译器会为开发人员处理这一点,使代码编写更加简单,阅读起来也更容易。苹果可能会继续沿着这条路走,我预计每年Objective-C都会变得更加精细。
这就是能够定义编译器和语言的优势。
在我看来,这样做的缺点是,随着语言的发展,对于新手开发人员来说,很难找到不引用旧特性的文档,尤其是当在网络上搜索示例代码时。尝试解密编译器错误可能会成为Objective-C新手开发人员的巨大挫折。

2
由于几个概念之间不兼容,Objective-C运行时禁止混合使用ARC和GC:
- GC的工作方式是-reatin/release/autorelease等不再起作用,因此引入了一个新概念-finalize来弥补这一缺陷; - ARC的工作方式是你不应该替换-retain/release/autorelease,编译器会为你发出命令(而且不能直接使用[object retain]等更低级的东西,只有在未替换-retain/release/autorelease时才能使用)。
这些原因意味着GC和ARC的ABI非常不同,不兼容。
ARC和MRR是兼容的(也就是说,你可以在使用ARC构建的代码和使用标准MRR构建的代码之间混合使用); GC和MRR是兼容的,因为基本上,在GC模式下运行时会忽略MRR调用; 但是GC和ARC不兼容。
当我说运行时禁止它时,我指的是在运行时,如果加载框架的应用程序是在GC模式下运行的GC应用程序,那么因为这些框架不支持GC并且不能为应用程序加载,所以没有任何框架可以使用ARC来编写。
副作用是,这意味着支持GC的Apple框架要么使用CF或MRR编写,要么对它们没有ARC的支持。

截至上述答案撰写时,我不确定,但实际上Objective-C运行时可以混合使用ARC和GC代码,但不能在同一_image_中。例如,您可以在ARC程序中加载一个GC编译的共享库,也可以在GC程序中加载一个ARC编译的共享库。 - paulotorrens

-1
ARC在进行垃圾回收时,通常会与UINavigationController发生冲突。
强引用(也称为var)将保留UIViewController指针在导航堆栈中,即使您已经解除或从堆栈中弹出了视图。这非常令人困惑。人们可能会认为弹出一个视图会将所有引用设置为nil。但实际上并不是这样。
使用ARC时,当视图消失时,您需要手动将强引用设置为nil(或在Objective-C中设置为NULL)。
或者也可以在deinit方法中设置。这取决于您如何组织代码。
一些不会被ARC释放保留计数的类的示例包括:
- NSTimer - AVPlayer - PHAsset - SceneKit - SpriteKit
总结一下,如果您使用简单的MVC(模型-视图-控制器)架构,并且只有一个视图控制器,那么ARC会进行垃圾回收。
如果您使用多个视图和控制器,则需要在使用ARC时将强引用设置为nil或NULL。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接