使用ARC还是不使用ARC?有什么优缺点?

113

我目前在工作的项目中使用的大部分代码都是在 iOS 5.0 之前编写的,因此我尚未使用 ARC。

我只是想知道,不需要手动保留/释放对象是否更便捷(并且伴随着更可靠的代码),这是否会抵消使用 ARC 的任何“成本”?您使用 ARC 的经验如何?是否推荐使用 ARC?

因此:

  • ARC 可以给项目带来多少好处?
  • 与 Java 中的垃圾回收一样,ARC 是否有成本?
  • 您是否一直在使用 ARC?如果是,迄今为止您的使用经验如何?

iPad/iOS上没有垃圾回收机制。你是在说OS X吗? - Sergey Kalinichenko
1
非常抱歉,我在错误地使用Java术语。我的意思是,使用ARC,对象可以在内存中保留比必要时间更长的时间,然后在稍后的自动释放池中作为一组被释放。我指的是它们与其他对象一起被保留和释放的效果,类似于Java的垃圾回收机制。 - Simon Withington
3
对于相同的代码(除了对release方法的调用),ARC会持有对象的时间不会比非ARC代码更长。实际上,它通常会比你提前释放。虽然有一些东西会稍微慢一点,但是它们加速了常见模式的执行速度,以至于性能影响几乎可以忽略不计。对于许多常见的模式(例如保留使用autorelease返回的对象),你甚至无法手动编写得比ARC自动执行得更快。 - Rob Napier
1
关于垃圾回收,请参考我对类似问题的回答:Objective-C自动引用计数和垃圾回收有什么区别? - Brad Larson
6个回答

149

没有缺点。使用它。今天就开始使用。它比你的旧代码更快。它比你的旧代码更安全。它比你的旧代码更容易。它不是垃圾收集。它没有GC运行时开销。编译器会在所有应该有的地方插入保留和释放代码。但它比你聪明,可以优化掉实际上不需要的部分(就像可以展开循环、消除临时变量、内联函数等一样)。

好的,现在我会告诉您一些小缺点:

  • 如果您是长期使用ObjC开发者,当您看到ARC代码时,您会感觉不适约一周时间。但您很快就会克服这个问题。

  • 与Core Foundation代码桥接时可能存在一些(非常)小的复杂性。处理任何将id视为void*的内容会稍微复杂一些。例如,id类型的C数组需要考虑更多细节才能正确处理。复杂的ObjCva_args处理可能也会引起麻烦。大多数涉及ObjC指针的数学计算都较为棘手。不过,在任何情况下,这些因素应该不会有太多问题。

  • 您不能将id放入struct中。这种情况非常罕见,但有时会用于数据打包。

  • 如果您没有遵循正确的KVC命名约定,并混合使用ARC和非ARC代码,则可能会发生内存问题。ARC使用KVC命名来确定内存管理方式。如果都是ARC代码,则无关紧要,因为它会在两侧都执行相同的“错误”操作。但如果混合使用ARC /非ARC,则会出现不匹配。

  • 在ObjC异常抛出期间,ARC会泄漏内存。ObjC异常应该非常接近程序的终止时间。如果您捕获了大量ObjC异常,则使用方式不正确。可以通过使用-fobjc-arc-exceptions进行修复,但会产生下面讨论的惩罚:

  • 在ObjC++代码中,ARC不会在ObjC或C++异常抛出时泄漏内存,但这是以时间和空间性能为代价的。这是另一个减少使用ObjC++的理由。

  • ARC在iPhoneOS 3或Mac OS X 10.5或更早版本上根本无法工作。(这使我无法在许多项目中使用ARC。)

  • __weak指针在iOS 4或Mac OS X 10.6上无法正常工作,这很遗憾,但相当容易解决。__weak指针很棒,但它们不是ARC的最大卖点。

  • 对于95%以上的代码来说,ARC非常出色,并且没有任何理由避免使用它(只要您可以处理操作系统版本限制)。对于非ARC代码,您可以按文件传递-fno-objc-arc。不幸的是,Xcode实际上比应该更难做到这一点。您应该将非ARC代码移动到单独的xcodeproj中,以简化此过程。

    总之,尽快切换到ARC,永远不要回头。


    编辑

    我看到有一些评论说“使用ARC不能替代了解Cocoa内存管理规则”,这基本上是正确的,但重要的是理解原因和不足之处。首先,如果您的所有代码都使用ARC,并且您在各个地方违反了三个魔术词,那么您仍然不会有问题。难以置信,但就是这样。ARC可能会保留您不想保留的一些东西,但它也会释放它们,所以这永远不会成为问题。如果我今天在教授Cocoa新课程,我可能只会在讨论KVC命名时提到内存管理命名规则而不会超过五分钟讲授实际内存管理规则。通过ARC,我相信您甚至可以成为一个不需要学习任何内存管理规则的入门级程序员。

    但您无法成为一个合格的中级程序员。您需要知道规则以便与Core Foundation正确连接,每个中级程序员在某个时候都需要处理CF。并且在处理混合ARC / MRC代码时需要知道规则。当您开始玩弄void *指向id的指针(您继续需要执行正确的KVO)时,您需要知道规则。块...好吧,块内存管理就是奇怪的。

    所以我的观点是,底层内存管理仍然很重要,但是在ARC之前,我花费了大量时间阐述规则以及为新程序员重新说明规则,现在它已成为一个更高级的主题。我宁愿让新开发者考虑对象图而不是填充他们的头脑与对objc_retain()的基本调用。


    3
    在非ARC中也需要特别小心,但在ARC中更需要注意的是保留循环。我的建议是:1)如果您发现自己将一个objc块存储为实例变量,请仔细检查它是否可能间接地捕获了它作为实例变量的对象。2)设计您的对象图,而不是让它自然发生。如果你想编写良好的代码,你必须知道谁拥有谁。 3)使用堆快照(heapshots):http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/ - Catfish_Man
    1
    @Catfish_Man 所说的都是好观点。保留循环一直以来都是一个问题。苹果的新建议正如您在#2中所说的那样。我们需要考虑对象图而不是保留和释放。#1是块的一个严重问题,也是我发现块是一种应该谨慎对待的双刃剑的几个原因之一。 - Rob Napier
    2
    有一个严重的缺点没有被提到:跨平台兼容性。对于我们这些勇敢而愚蠢的人来说,面对着被称为跨平台编程的荒原,ARC根本不是一个选择,至少在GNU再次扩展他们的ObjC运行时和编译器功能之前不是(他们说还有更多的功能正在路上)。 - Rabbit
    2
    除了需要支持iOS 3.x设备外,你可以使用ARC。顺便说一下,确保使用Xcode进行转换(编辑 > 重构 > 转换为Objective-C ARC)。在此过程中,Xcode会进行大量验证,以确保你的代码能够正常工作,并找出任何违反设计准则的代码。 - ZhangChn
    1
    很好,回答得非常到位。这是我给出的第100个点赞。祝贺你迎来了一个世纪的荣耀 ;) - Ajay Sharma
    显示剩余6条评论

    20
    比我更专业的技术答案可能会出现,但是这里是我的回答:
    • ARC != 垃圾收集。它没有运行时惩罚,它在编译时完成。
    • ARC 也不是仅仅自动释放所有东西,如你在评论中所建议的那样。请阅读文档
    • 一旦你意识到自己曾经做了多少手动引用管理,就会觉得很棒。
    • 使用它吧!
    • 一个缺点是,维护旧的非 ARC 代码突然变得非常繁琐。

    是的,这就是为什么到目前为止我还没有使用它的原因——我不确定我能够忍受整个巨大的现有代码库的转换... 不过下一个项目我一定会尝试一下。谢谢您的回答 :) - Simon Withington
    并不是维护旧代码变得更加困难了,而是你的期望值发生了改变。因此,归咎于 ARC 让生活变得更轻松并不公平。当你准备好时,Xcode 可以轻松地将旧代码转换为 ARC,但如果你仍然需要支持旧版 iOS,则无法使用它。 - Caleb
    1
    @Caleb 我并不是在责怪 ARC,只是这是我迄今为止看到的唯一缺点——在一个项目中它已经让我变得非常挑剔了。 - jrturton
    是的,我没有将其列为问题,但是在需要在代码库之间切换时存在内存管理实践的小问题。我的一些项目在10.4上运行,因此我仍然需要进行大量的Objective-C 1.0工作(因此没有@synthesize,更不用说ARC)。但是你会很好地习惯切换模式。 - Rob Napier
    @jrturton,我应该更清楚地表明我写这篇文章是为了让原帖作者和未来的读者更好地理解ARC的作用。我知道你的意思,只是不想让任何人得出混合使用ARC和非ARC代码会产生问题的错误结论。 - Caleb

    17

    ARC能为项目带来多大的好处?

    ARC可以显著降低因未释放对象而导致的泄漏和因无法保留或过早释放对象而导致的崩溃。但仍需理解基于引用计数的内存模型,以便分类强引用和弱引用、避免循环引用等。

    自动垃圾回收真正的“代价”有多大?

    iOS没有垃圾回收机制。ARC类似于垃圾回收,因为你不需要手动保留或释放对象。但与垃圾回收不同的是,没有垃圾回收器。仍然适用保留/释放内存模型,只是编译器在编译时会为你插入相应的内存管理调用。

    您是否一直在使用ARC?如果是,迄今为止您的体验如何?

    如果您习惯了引用计数,那么使用ARC可能会让人有些不安,但这只是一个适应过程,需要学会信任编译器会自动进行正确的操作。它感觉像Objective-C 2.0引入属性的延续,是简化内存管理的又一大进步。没有手动内存管理调用,您的代码会更短、更易读。

    ARC唯一的问题是不支持旧版本的iOS,因此在决定是否采用时需要考虑这一点。


    1
    这就是我所说的更好的答案! - jrturton
    1
    是的,非常好的答案。那么除了需要学会相信它的工作之外,ARC真的没有任何缺点吗?在这种情况下,它似乎太好了,难以置信? - Simon Withington

    4
    我认为ARC是一个很好的想法。与GC相比,你可以两全其美。我倾向于认为MRC对于内存管理施加了一种宝贵的"纪律",每个人都能从中受益。
    但我也同意,需要注意的真正问题是对象所有权和对象图(正如许多人指出的),而不是低级引用计数本身。
    总之:ARC并不是关于内存不用考虑的自由通行证;它是一种工具,帮助人类避免重复的任务导致的压力和容易出错,从而更好地委托给机器(在这种情况下是编译器)。
    话虽如此,就我个人而言,我还没有做出转变。我刚刚开始使用Git...
    更新:因此,我迁移了整个游戏,包括gl库,在Xcode 4.2中迁移助手除外,目前还没有发现问题。如果您要开始新项目,请尝试使用ARC。

    2
    我遇到的唯一缺点是,如果您使用了许多CoreFoundation函数和数据的库。在MRC中,您不需要担心使用CFStringRef而不是NSString*。但在ARC中,您必须指定这两者如何交互(基本桥接?释放CoreFoundation对象并将其移动到ARC?将Cocoa对象作为+1 CoreFoundation保留对象?)。此外,在OS X上,它仅适用于64位代码(尽管我有一个可以解决这个问题的头文件…)。

    2

    我已经在一些(尽管是小型的)项目中使用它,我的经验非常好,无论是性能还是可靠性方面都很优秀。

    一个小小的注意事项是,如果你自己编写UI,你需要学习弱引用的做与不做,以避免引用循环。如果你使用设计师设置GUI,他们通常会自动完成这个工作。


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