在Cocoa中使用自动释放的成本是多少?

时间:2008-10-10 23:00:13

标签: objective-c cocoa memory-management

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

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

最终我应该使用一种策略,其中所有内容都是自动释放的,并且对于特定情况,使用retain / release应该是规则的例外吗?或者我是否应该使用自动释放的retain / release作为[NSString stringWithEtc ...]等方便方法返回对象的例外?

11 个答案:

答案 0 :(得分:21)

有两项费用:

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

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

  3. 任何平台上的最佳做法是尽可能避免自动释放。

    回答问题:

      

    最终我应该使用一种策略,其中所有内容都是自动释放的,并且对于特定情况,使用retain / release应该是规则的例外吗?

    恰恰相反。

      

    或者我通常应该使用自动释放的retain / release是来自[NSString stringWithEtc ...]等便利方法的返回对象的例外吗?

    如果可以,总是总是使用保留/释放 - 在NSString的情况下,通常不需要使用stringWithEtc方法,因为{{1}等价物。

    另见this question

答案 1 :(得分:15)

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

然后,只有我有一个紧凑的循环,我需要优化,我将开始考虑性能。否则这只是过早的优化,这通常被认为是一件坏事。

答案 2 :(得分:9)

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

如果你有一个永远不会自动释放的过度释放的物体,那么追踪它很容易。如果你有一个用户报告的崩溃,在NSPopAutoreleasePool以南的某个地方间歇性地发生了回溯,祝你好运...

答案 3 :(得分:8)

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

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

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

答案 4 :(得分:7)

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

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

当然,这完全适用于Mac应用程序。在内存较为紧张的iPhone应用程序中,您可能希望在使用自动释放时保守一些。但与往常一样,首先编写可读代码,然后再进行优化,测量缓慢/内存密集的部分。

答案 5 :(得分:6)

费用如下:

  1. 找到当前线程的自动释放池并将对象添加到其中的时间。
  2. 对象占用的内存,直到稍后释放为止。
  3. 如果你想对你的内存使用非常保守,你应该避免自动释放。但是,它是一种有用的技术,可以使代码更具可读性。痴迷使用保留/释放属于“过早优化”的范畴。

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

    警惕自动释放的时间是你在循环中。例如,您正在迭代用户的地址簿,并且可能为每个条目加载图像文件。如果所有这些图像对象都是自动释放的,它们将累积在内存中,直到您访问整个地址簿。如果地址簿足够大,则可能会耗尽内存。如果您在完成图像后立即释放图像,则在循环内,您的应用程序可以回收内存。

    如果你无法避免在循环中自动释放(它是由你没有编写但不能改变的代码完成的),你也可以在需要时自己管理循环中的NSAutoreleasePool。

    因此,请注意在循环中使用自动释放(或者可以从循环中调用的方法),但是当它可以使代码更具可读性时,请不要避免使用它。

答案 6 :(得分:2)

据我了解,使用自动释放的主要缺点是你不知道什么时候最终会释放和销毁对象。这可能会导致您的应用程序使用大量内存,如果您有许多自动释放的对象挂起但尚未释放。

答案 7 :(得分:2)

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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

旧线程,但为了新读者的利益而削减。

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

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

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

无论如何,我认为一刀切的做法并不合适。您可以使用任何方法 - 或方法组合 - 适合项目,并牢记上述所有因素。