11

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

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

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

4

11 回答 11

21

有两种费用:

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

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

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

要回答问题:

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

恰恰相反。

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

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

另请参阅此问题

于 2008-10-11T04:23:37.030 回答
15

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

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

于 2008-10-12T16:23:40.353 回答
9

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

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

于 2008-10-11T07:42:29.830 回答
8

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

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

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

于 2008-10-22T19:47:33.383 回答
7

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

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

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

于 2008-10-22T04:57:44.413 回答
6

费用是:

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

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

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

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

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

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

于 2008-10-11T07:36:09.887 回答
2

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

于 2008-10-10T23:44:40.683 回答
2

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

于 2008-10-12T13:10:23.583 回答
1

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

于 2008-10-15T14:17:00.153 回答
0

要记住的一个注意事项是,如果您正在生成一个新线程,则必须在执行任何其他操作之前在该线程上设置一个新的自动释放池。即使您不使用自动释放对象,也可能是 Cocoa API 中的某些东西。

于 2008-10-15T13:57:17.043 回答
0

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

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

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

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

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

于 2010-03-30T15:53:57.540 回答