大多数Apples文档似乎都避免使用自动释放的对象,尤其是在创建gui视图时,但我想知道使用自动释放对象的成本是多少?
UIScrollView *timeline = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 20, 320, 34)]; [self addSubview:timeline]; [timeline release];
最终我应该使用一个策略,其中所有内容都是自动释放的,并且使用retain/release应该是特定情况的规则的例外吗?或者我通常应该使用自动释放的retain/release是来自[NSString stringWithEtc ...]等便利方法的返回对象的例外吗?
有两个成本:
(假设您可以选择避免自动释放的对象.)您实际上会不必要地延长对象的生命周期.这可能意味着您的内存占用增长 - 不必要.在受限平台上,这可能意味着如果应用程序超出限制,则终止应用程序.即使您没有超出限制,也可能导致系统开始交换,这是非常低效的.
查找当前自动释放池,向其添加自动释放对象,然后在最后释放对象(额外的方法调用)的额外开销.这可能不是一个很大的开销,但它可以加起来.
任何平台上的最佳做法是尽可能避免自动释放.
回答问题:
最终我应该使用一个策略,其中所有内容都是自动释放的,并且使用retain/release应该是特定情况的规则的例外吗?
恰恰相反.
或者我通常应该使用自动释放的retain/release是来自[NSString stringWithEtc ...]等便利方法的返回对象的例外吗?
如果可以,您应该始终使用保留/释放 - 在NSString
通常不需要使用stringWithEtc
方法的情况下,因为有initWithEtc
等价物.
另见这个问题.
我不同意Jim Puls的观点 - 我认为不使用Autorelease会使调试变得更加困难,因为你更有可能发现自己不小心泄漏了内存.当然Clang静态分析器可以选择其中一些实例,但对我来说,习惯使用自动释放的轻微开销成本远远超过我的代码不太可能出错.
然后,只有我有一个紧密的循环,我需要优化,我将开始考虑性能.否则这只是过早的优化,这通常被认为是一件坏事.
我很惊讶没人提到这个.当你可以与性能无关时,避免自动释放对象的最大原因.是的,这里提到的所有性能问题绝对有效,但自动释放的最大缺点是它使调试变得更加困难.
如果你有一个永远不会自动释放的过度释放的物体,那么追踪它很容易.如果你有一个用户报告的崩溃,在NSPopAutoreleasePool以南的某个地方间歇性地发生了回溯,祝你好运......
这些天我通常使用自动释放的对象,因为它们往往会导致更简单,更容易阅读的代码.你声明并初始化它们,然后放弃范围.在机械上它们存在的时间要长一些,但是从编写代码的人的角度来看,它等同于C++中的堆栈声明对象在函数返回并且其框架被销毁时自动被破坏.
虽然存在效率损失,但在大多数情况下并不重要.更大的问题是更现存的对象和后来的内存恢复可能导致更加碎片化的地址空间.如果这是一个问题,通常很容易进入并切换到一些热门方法中的手动保留/释放并改进它.
正如其他人所说,可读性胜过非性能敏感代码的性能.在许多情况下,使用自动释放的对象会导致更多的内存碎片,但在任何情况下,对象都将比池更长,它不会.在这些情况下,您支付的唯一价格是找到找到正确的自动释放池的成本.
使用自动释放池的一个好处是它们在不使用@try
/的情况下是异常安全的@finally
.Greg Parker('Mr. Objective-C')有一篇很好的帖子解释了这个细节.
autorelease
IMO,我倾向于使用更少的代码,使其更具可读性.正如其他人所指出的那样,缺点是你延长了对象的生命周期,因此暂时使用了更多的内存.在实践中,我还没有autorelease
在我编写的任何Mac应用程序中发现过度使用是一个重要问题.如果高内存使用确实是一个问题(这不是真正的泄漏造成的),我只需添加更多自动释放池(在分析后向我展示我需要它们).但是,总的来说,这种情况非常罕见.正如Mike Ash的帖子所显示的那样(Graham Lee与之相关),自动释放池的开销非常小而且速度很快.添加更多自动释放池的成本几乎为零.
当然,这适用于Mac应用程序.在内存较为紧张的iPhone应用程序中,您可能希望在使用自动释放时保守一些.但与往常一样,首先编写可读代码,然后通过测量慢速/内存密集型部件的位置进行优化.
费用是:
找到当前线程的自动释放池并将对象添加到它的时间.
对象占用的内存,直到稍后释放.
如果你想对你的内存使用非常保守,你应该避免自动释放.但是,它是一种有用的技术,可以使代码更具可读性.痴迷使用保留/释放属于"过早优化"的保护范围.
如果您在Cocoa的主事件处理线程(您大部分时间都是这样),则当控制返回到事件处理程序时,自动释放池将被清空.如果您的方法很短并且不会循环大量数据,那么使用autorelease将deallocation延迟到运行循环的末尾就可以了.
警惕自动释放的时间是你在循环中.例如,您正在迭代用户的地址簿,并且可能为每个条目加载图像文件.如果所有这些图像对象都是自动释放的,它们将累积在内存中,直到您访问整个地址簿.如果地址簿足够大,则可能会耗尽内存.如果您在完成图像后立即释放图像,则在循环内,您的应用程序可以回收内存.
如果你无法避免在循环中自动释放(它是由你没有编写但不能更改的代码完成的),你也可以在需要时自己管理循环中的NSAutoreleasePool.
因此,请注意在循环中使用自动释放(或者可以从循环中调用的方法),但是当它可以使代码更具可读性时不要避免使用它.