【问题标题】:What is the cost of using autorelease in Cocoa?在 Cocoa 中使用 autorelease 的成本是多少?
【发布时间】:2010-09-16 15:16:17
【问题描述】:

大部分 Apple 文档似乎都避免使用自动释放对象,尤其是在创建 gui 视图时,但我想知道使用自动释放对象的成本是多少?

UIScrollView *timeline = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 20, 320, 34)];
[self addSubview:timeline];
[timeline release];

最终我应该使用自动释放所有内容的策略,并且在特定情况下使用保留/释放应该是规则的例外吗?还是我通常应该使用保留/释放,而自动释放是从 [NSString stringWithEtc...] 等便捷方法返回的对象的例外?

【问题讨论】:

  • 等等,这只是普通的垃圾回收吗?
  • 不,这不是垃圾回收。

标签: objective-c cocoa memory-management


【解决方案1】:

据我了解,使用自动释放的主要缺点是您不知道对象何时最终会被释放和销毁。如果您有很多自动释放但尚未释放的对象,这可能会导致您的应用使用比所需更多的内存。

【讨论】:

    【解决方案2】:

    有两种费用:

    1. (假设您可以选择避免自动释放对象。)您实际上不必要地延长了对象的生命周期。这可能意味着您的内存占用量会不必要地增加。在受限平台上,这可能意味着如果您的应用程序超出限制,则会终止。即使你没有超过限制,也可能导致你的系统开始交换,这是非常低效的。

    2. 查找当前自动释放池、将自动释放的对象添加到其中、然后在最后释放对象(额外的方法调用)的额外开销。这可能不是很大的开销,但可以加起来。

    任何平台上的最佳实践都是尽可能避免自动释放。

    回答问题:

    最终我应该使用自动释放所有内容的策略,并且在特定情况下使用保留/释放应该是规则的例外吗?

    恰恰相反。

    或者我是否应该通常使用保留/释放,而自动释放是从 [NSString stringWithEtc...] 等便捷方法返回的对象的例外?

    如果可以,您应该始终使用保留/释放 - 在 NSString 的情况下,通常不需要使用 stringWithEtc 方法,因为有 initWithEtc 等效项。

    另见this question

    【讨论】:

    • 你的两个原因归结为过早地优化性能而不是代码可读性。在重要的时候,他们都有一个简单的解决方案:NSAutoreleasePool。
    【解决方案3】:

    费用为:

    1. 定位当前线程的自动释放池并将对象添加到其中的时间。
    2. 对象在稍后释放之前占用的内存。

    如果您想非常保守地使用内存,则应避免自动释放。但是,它是一种有用的技术,可以使代码更具可读性。过分使用保留/释放属于“过早优化”的范畴。

    如果你在 Cocoa 的主事件处理线程中(你大部分时间都在),当控制权返回到事件处理程序时,自动释放池被清空。如果您的方法很短并且不会循环大量数据,那么使用 autorelease 将释放延迟到运行循环的末尾就可以了。

    警惕自动释放的时间是当您处于循环中时。例如,您正在遍历用户的通讯录,并可能为每个条目加载一个图像文件。如果所有这些图像对象都是自动释放的,它们将在内存中累积,直到您访问了整个地址簿。如果通讯簿足够大,您可能会耗尽内存。如果您在处理完图像后立即释放它们,那么在循环中,您的应用可以回收内存。

    如果您无法避免在循环内自动释放(这是由您未编写且无法更改的代码完成的),您还可以根据需要在循环内自行管理 NSAutoreleasePool。

    因此,请注意在循环(或可能从循环中调用的方法)内使用 autorelease,但不要避免使用它,因为它可以使代码更具可读性。

    【讨论】:

    • 这不被认为是“最佳实践”,尤其是在资源受限的平台上。还要注意 Jim Puls 对调试的观察。
    • 什么不是“最佳实践”?我是说对于自动发布的成本(我们同意),您可以提高代码的可读性。有时这是一个可以接受的权衡。
    • 使用自动释放不是最佳实践。
    • 避免自动释放不是最佳实践。
    • Cocoa 内存管理的最佳实践是使用autorelease,以及相关的便利方法等。最佳实践是切换到手动管理retain/release if, 且仅当来自分析工具的客观数据表明自动释放池热点。即便如此,还是首选使用精心放置的NSAutoreleasePool。忘记@mmalc 的意思吧——我的 iPhone/iPod touch 比我的第一台运行 OS X 的 Mac 拥有更多的内存……而且没有人为这些机器编写的程序与为今天的机器编写的程序有任何不同。
    【解决方案4】:

    我很惊讶还没有人提到这一点。当你可以避免自动释放对象的最大原因与性能无关。是的,这里提到的所有性能问题绝对有效,但自动释放的最大缺点是它使调试变得更加困难。

    如果您有一个从不自动释放的过度释放对象,那么追踪起来非常容易。如果您有用户报告的崩溃,并且在 NSPopAutoreleasePool 以南某处回溯间歇性发生,那么祝您好运...

    【讨论】:

    • 这在 Leopard 时代是一个正当的理由,当时 Xcode 还没有集成静态分析器并且 Instruments 很烂。现在,使用 Instruments 消除现有的内存错误处理错误并通过打开并遵守静态分析器从一开始就防止错误的存在并不难。
    【解决方案5】:

    其他人已经回答你是否应该自动释放,但是当你必须自动释放时,尽早排水并经常排水:http://www.mikeash.com/?page=pyblog/autorelease-is-fast.html

    【讨论】:

      【解决方案6】:

      我不得不不同意 Jim Puls 的观点 - 我认为 使用 Autorelease 会使调试更加困难,因为您更有可能发现自己意外泄漏内存。当然 Clang 静态分析器可以提取其中一些实例,但对我来说,习惯使用自动释放的轻微开销成本远远超过了我的代码不太可能出现错误的情况。

      然后,只有当我有一个需要优化的紧密循环时,我才会开始关注性能。否则这一切都只是过早的优化,这通常被认为是一件坏事。

      【讨论】:

      • 但是不小心泄漏内存不是比发生无法追踪的崩溃更可取吗?我只是根据经验说话。
      • 吉姆:我同意你的看法,这也是我的经验
      【解决方案7】:

      要记住的一点是,如果您要生成一个新线程,则必须先在该线程上设置一个新的自动释放池,然后再执行其他任何操作。即使您没有使用自动释放对象,也可能是 Cocoa API 中的某些内容。

      【讨论】:

        【解决方案8】:

        我注意到您提供的代码示例适用于 iPhone。 Apple 特别建议避免为 iPhone 应用程序自动释放对象。我找不到具体的原因,但他们在 WWDC 上强调了这一点。

        【讨论】:

          【解决方案9】:

          使用自动释放池的一个好处是它们在不使用@try/@finally 的情况下是异常安全的。 Greg Parker('Mr. Objective-C')有一个great post 解释了这个细节。

          我倾向于大量使用autorelease,因为它的代码更少,并且更具可读性,IMO。正如其他人指出的那样,缺点是您延长了对象的生命周期,因此暂时使用了更多内存。在实践中,我还没有发现过度使用autorelease 是我编写的任何 Mac 应用程序中的一个重要问题。如果高内存使用似乎确实是一个问题(这不是由真正的泄漏引起的),我只需添加更多的自动释放池(在分析后向我展示我需要它们的位置)。但是,总的来说,这种情况非常罕见。正如 Mike Ash 的帖子所示(Graham Lee 链接到它),自动释放池的开销很小并且速度很快。添加更多自动释放池的成本几乎为零。

          当然,这一切都适用于 Mac 应用程序。在内存更紧张的 iPhone 应用程序中,您可能希望在使用自动释放时保持保守。但与往常一样,先编写可读代码,然后再优化,通过测量慢速/内存密集型部分的位置。

          【讨论】:

            【解决方案10】:

            我现在通常使用自动释放的对象,因为它们往往会导致更简单、更易于阅读的代码。您声明并初始化它们,然后让 drop 超出范围。从机械上讲,它们存在的时间要长一些,但从编写代码的人的角度来看,它相当于在 C++ 中声明的堆栈对象在函数返回并且其框架被销毁时自动被销毁。

            虽然存在效率损失,但在大多数情况下并不显着。更大的问题是存在的对象越多,以后的内存恢复可能会导致地址空间更加碎片化。如果这是一个问题,通常很容易进入并通过一些热门方法切换到手动保留/释放并改进它。

            正如其他人所说,在非性能敏感代码中,可读性胜过性能。在许多情况下,使用自动释放的对象会导致更多的内存碎片,但在任何情况下,对象的寿命都不会超过池。在这些情况下,您付出的唯一代价就是找到找到正确的自动释放池的成本。

            【讨论】:

              【解决方案11】:

              旧线程,但为了新读者的利益而努力。

              我使用自动释放与保留/释放取决于特定于对象的自动释放错误的风险和对象的大小。如果我只是在我的视图中添加一些微小的 UIImageView 或几个 UILabel,自动释放可以使代码保持可读性和可管理性。当视图被移除和释放时,这些子视图应该很快就会被释放。

              另一方面,如果我们谈论的是 UIWebView(自动释放错误的高风险),或者当然是一些需要持久保存到对象“死亡”的数据,那么保留/释放是要走的路.

              老实说,我的项目还没有那么大,自动释放对象的额外“停留时间”会产生内存问题。对于复杂的应用程序,这种担忧是合理的。

              无论如何,我不认为一刀切的方法是正确的。您可以使用适合项目的任何方法(或方法组合),同时牢记上述所有因素。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-06-27
                • 2012-04-02
                • 2011-04-10
                • 1970-01-01
                • 2011-05-17
                • 2012-05-13
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多