使用ARC,每个线程都没有自动释放池是致命的吗?

时间:2012-09-25 01:18:25

标签: ios multithreading cocoa-touch nsautoreleasepool

我读到了这个:

  

如果您在应用程序中创建了辅助线程,则需要为其提供自己的自动释放池。自动释放池及其包含的对象将在

中进一步讨论

在iOS 5开发者食谱中。

我正在使用ARC进行编译。我一直在创建许多后台线程,似乎我做得很好。我的后台线程都没有长时间运行。所有这些对象是否会被主线程的自动释放池释放?或者是什么?

这就是我所做的调用后台线程的方法:

+(void)doBackground:(void (^)())block
{
    //DISPATCH_QUEUE_PRIORITY_HIGH
    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
    dispatch_async(dispatch_get_global_queue(-2,0), ^{
        block();
    });
}

我应该将其更改为

+(void)doBackground:(void (^)())block
{
    //DISPATCH_QUEUE_PRIORITY_HIGH
    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
    dispatch_async(dispatch_get_global_queue(-2,0), ^{
        @autoreleasepool{
        block();
        }
    });
}

2 个答案:

答案 0 :(得分:34)

如果您没有为新线程创建自动释放池,请至少考虑它是程序员错误。这对你的程序是否致命是由程序的实现来定义的。经典问题是泄漏的对象,因此对象'dealloc从未执行过(可能是致命的)。

在ARC下创建自动释放池的现代方法是:

void MONThreadsEntry() { // << entry is e.g. a function or method
  @autoreleasepool {
    ...do your work here...
  }
}

更详细地说,自动释放池表现为线程本地堆栈 - 您可以推送和弹出,但在该线程上的任何内容被自动释放之前应始终存在一个。自动释放消息从一个线程传输到另一个线程。

如果“创建线程”的想法是使用更高级别的异步机制(例如使用NSOperationQueue),或者底层实现创建了辅助线程,那么您可能看不到问题(例如在控制台或泄漏中)自己的自动释放池。

无论如何,不​​要猜测何时创建自动释放池,而只是了解创建它们的位置以及何时应该创建它们。这一切都很明确 - 没有必要进行猜测,也没有必要担心创造它们。

同样,如果您使用较低级别的抽象,并且永远不会在该线程上自动释放对象,则永远不需要为您的线程创建自动释放池。例如,pthreads和纯C实现不需要打扰自动释放池(除非您使用的某些API假定它们已就位)。

即使是Cocoa应用程序中的主线程也需要一个自动释放池 - 它通常不是你编写的东西,因为它存在于项目模板中。

更新 - 调度队列

响应更新的问题:是的,您仍然应该为在调度队列下运行的程序创建自动释放池 - 请注意,使用调度队列,您不会创建线程,因此这与原始问题完全不同题。原因:虽然调度队列执行管理自动释放池,但no guarantee是关于它们被清空的时间/点。也就是说,您的对象将被释放(在某些时候),但您应该也在此上下文中创建自动释放池,因为实现可能(理论上)每运行10,000个块就耗尽池,或大约每天。所以在这种情况下,在你最终消耗太多内存,或者你的程序期望它的对象将以某种确定的方式被破坏的情况下,它实际上只是致命的 - 例如,你可能正在加载或处理图像如果由于自动释放池而导致这些图像的寿命意外延长,那么背景和最终消耗大量内存。另一个示例是共享资源或全局对象,您可以在其中响应通知或引入竞争条件,因为“阻止本地”对象可能比您预期的时间长得多。还要记住,实现/频率可以随着它的实现者认为合适而自由改变。

答案 1 :(得分:4)

现在似乎自动为新线程创建自动释放池。不知道什么时候发生了变化,为什么文档说明了相反的情况,但就是这样。