iOS 编程自动释放池

Posted

技术标签:

【中文标题】iOS 编程自动释放池【英文标题】:iOS programming autorelease pool 【发布时间】:2012-08-31 16:05:29 【问题描述】:

在Apple documentation 中,它说:

AppKit 和 UIKit 框架处理每个事件循环迭代 (例如鼠标按下事件或点击)在自动释放池中 堵塞。因此,您通常不必创建自动释放 池块自己,甚至查看用于创建一个的代码。

现在,这应该是显而易见的,但无论如何我都会要求确认。

如果我正在开发最终将成为后台进程的内容(通过 Grand Central Dispatch),但为简单起见,我首先将其放在第一个加载视图的 viewDidLoad 中,这样我的视图实际上不会显示在屏幕直到所有(比如说)2分钟的处理完成,然后在这两分钟内所有默认的自动释放池永远不会被释放,因为它还没有经过事件循环迭代,对吧?听到拒绝简直是疯了,但我陷入了这个日益严重的记忆问题,所以我会要求确认并希望有一个好消息。

如果是这样,我应该放置我自己的自动释放块。如果处理是通过 GCD 在后台进程中完成的,我仍然需要自动释放块,对吗?

【问题讨论】:

我假设你没有使用 ARC 对吧? 至少在 Xcode 3 下,如果您在模拟器中运行您的应用程序并创建了一个自动释放对象,其中“堆栈”中没有自动释放池,模拟器将在控制台中打印出警告大致如下:“没有定义自动释放池——只是泄漏了对象 xxx。”缩小这些来源的范围可能很麻烦,但这是一个非常有用的信息,可以确保您没有得到。 【参考方案1】:

我会尽力为您提供两个问题的完整答案。

第一部分。首先要注意主线程中长时间运行的操作。例如,如果您的操作需要两分钟,主线程将被阻塞,直到它完成。从用户的角度来看,应用程序将在两分钟内没有响应。 无论如何,是的,在应用程序委托中有一个插入自动释放对象的池。当循环结束时,池内的对象会被释放,因为池会自动排空。 如果您有内存问题,可以查看Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint。如文档中所写,您应该将操作包装在自动释放块中。在块结束时,临时对象被释放,这通常会导致它们的释放,从而减少程序的内存占用。

关于 GCD 问题,我会说不。处理 GCD 时不必创建自动释放池。通常,正如Do you need to create an NSAutoreleasePool within a block in GCD? 中所写,GCD 会自动管理每个队列的自动释放池。所以,如果你的对象很少,你不必担心,但如果你创建了很多,是的,创建一个自动释放池。后者还可以让您减少内存占用。

因此,在应用无法响应的整整两分钟内, 这是当循环尚未结束,并且池在期间没有排干 这两分钟,对吧?

应用程序没有响应,因为主线程(通过运行循环)按顺序执行任务。如果您阻止运行循环,应用程序会冻结,直到长时间运行的操作完成(我认为如果您超过特定时间段,应用程序会被 ios 杀死)。为避免这种情况,您可以(如您所写)在不同的线程中执行长时间运行的操作。

使用线程的目的是使应用程序具有高响应性,但它​​可能导致各种问题,例如不一致的数据(竞争条件)或死锁

我真的建议阅读The pogo stick of NSRunLoop、Understanding NSRunLoop 和NSDefaultRunLoopMode vs NSRunLoopCommonModes 了解更多信息。

【讨论】:

我需要澄清你的解释。因此,对于应用程序没有响应的整整两分钟,就是循环还没有结束,并且在这两分钟内池没有耗尽,对吧? @huggie 我扩展了我的答案。希望对您有所帮助。 太棒了!我感谢您的帮助!我放了更多的自动释放块,现在我的程序在没有呕吐的情况下完成了整个过程。现在仪器报告在我的测量期间实时 RAM 使用量下降到 2MB,而 OS X 的活动监视器显示 RAM 使用量约为 70 MB,而以前需要高达 1GB 的 RAM。【参考方案2】:

您不需要创建自己的自动释放块来管理自动释放对象的内存; 一个池,它是自动管理的,自动释放的对象最终会被释放。

因此,唯一的问题是“最终”这个神奇的词。在您的处理过程中,您的应用程序是否因自动释放对象尚未被释放而使用了过多的内存(例如,系统会杀死它的内存)?请记住,他们被释放;问题是情况是否如此糟糕,以至于您需要现在释放它们,而无需等待自动释放池的耗尽,而自动释放池肯定会发生,而无需您采取任何行动.

只有 Instruments 可以告诉你,所以不要猜测,不要过早优化;看看仪器揭示了什么。如果情况如此糟糕,那么可以肯定的是,找到生成所有这些自动释放对象的循环并在其中放置一个自动释放块,以便每次通过循环时都会耗尽池。否则,真的没有必要。 (另一方面,在 ARC 下,自动释放块增加的开销很小,因此过早优化也可能不会造成太大的危害。)

【讨论】:

创建/管理自动释放池的成本是最低的,因此只要保持正确性(即不要过早自动释放),在使用中稍微“过度”并没有真正的危险)。特别是,如果有一个相对长时间运行的循环将创建大量本地对象,则添加自动释放范围可能是明智之举,而无需先进行检测。 事实上,当在我的模拟器中运行时,它运行到无法再分配的地步!所以是的,它使用了太多的内存。例如,我在想我可能需要将所有 stringWithFormat 调用更改为 initWithFormat。或者我需要我自己的自动释放块。 @huggie -- initWithFormat 不会有任何好处(事实上,会更糟),除非你在某处有相应的release @HotLicks 这个项目有 ARC,所以我不会写 release。但只有当我真的需要它并保留它时,我才会认为它是一样的。怎么会更糟?

以上是关于iOS 编程自动释放池的主要内容,如果未能解决你的问题,请参考以下文章

iOS自动释放池_原理_如何工作

iOS自动释放池_原理_如何工作

读书笔记iOS-自动释放池

iOS中单独线程的自动释放池[重复]

iOS之深入解析自动释放池autoreleasepool的底层原理

iOS中的所有对象都会自动添加到自动释放池中吗?