现代iOS 8 NSOperation使用仍然需要@autoreleasepool吗?

Max*_*ler 25 nsoperation nsautoreleasepool ios automatic-ref-counting ios8

我已阅读并发编程指南

在指南中,文本指出GCD调度队列定义了他们自己的@autoreleasepool池,并提到仍然建议在每个调度级别定义一个,但是对于NSOperation没有说什么,Apple提供的示例代码也没有显示@autoreleasepool结构.在NSOperation的背景下,模糊地提到@autoreleasepool的唯一地方是修订历史,

2012-07-17 - 删除了有关自动释放池使用操作的过时信息.

查看在线提供的示例代码,例如 http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues在基于NSOperations的对象的实现中使用@autoreleasepool,例如:

@implementation ImageDownloader

- (void)main {
    @autoreleasepool {
      ...
    }
}     
@end
Run Code Online (Sandbox Code Playgroud)
  1. 我应该如何实现现代NSOperation对象?
  2. Apple在2012-07-17之间提到的更新是什么?

Stu*_*nie 36

如果您从NSOperationmain方法派生并实现该方法,则无需设置自动释放池.该start方法的默认实现推送NSAutoReleasePool,调用main然后弹出NSAutoReleasePool.同样适用于NSInvocationOperationNSBlockOperation,它们共享相同的start方法实现.

以下是该start方法的简略反汇编NSOperation.注意调用NSPushAutoreleasePool,然后调用main,然后调用NSPopAutoreleasePool:

Foundation`-[newMyObj__NSOperationInternal _start:]:
0x7fff8e5df30f:  pushq  %rbp

...

0x7fff8e5df49c:  callq  *-0x16b95bb2(%rip)        ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df4a2:  movl   $0x1, %edi

; new NSAutoreleasePool is pushed here
0x7fff8e5df4a7:  callq  0x7fff8e5df6d6            ; NSPushAutoreleasePool

... NSOperation main is called

0x7fff8e5df6a4:  callq  *-0x16b95dba(%rip)        ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df6aa:  movq   %r15, %rdi

; new NSAutoreleasePool is popped here, which releases any objects added in the main method
0x7fff8e5df6ad:  callq  0x7fff8e5e1408            ; NSPopAutoreleasePool
Run Code Online (Sandbox Code Playgroud)

以下是一些运行示例代码的快照

  1. MyObjmain方法中分配,我确保对象必须自动释放
  2. main返回到_start,并且下图显示了MyObj dealloc当前自动释放池调用的堆栈跟踪,弹出内部_start

调用NSPopAutoreleasePool时释放调用堆栈显示对象

作为参考,这是我用来验证行为的示例代码:

#import <Foundation/Foundation.h>

@interface MyObj : NSObject
@end

@implementation MyObj

- (void)dealloc {
    NSLog(@"dealloc");
}

@end

@interface TestOp : NSOperation {
    MyObj *obj;
}

@end

@implementation TestOp

- (MyObj *)setMyObj:(MyObj *)o {
    MyObj *old = obj;
    obj = o;
    return old;
}

- (void)main {
    MyObj *old = [self setMyObj:[MyObj new]];
    [self setMyObj:old];
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");

        NSOperationQueue *q = [NSOperationQueue new];
        TestOp *op = [TestOp new];
        [q addOperation:op];

        [op waitUntilFinished];
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

根据并发编程指南,Grand Central Dispatch类似地管理调度队列的自动释放池:

如果您的块创建了多个Objective-C对象,您可能希望将块的代码部分包含在@autorelease块中,以处理这些对象的内存管理.尽管GCD调度队列具有自己的自动释放池,但它们无法保证这些池何时耗尽.如果您的应用程序受内存限制,则创建自己的自动释放池允许您以更加规则的间隔释放自动释放对象的内存.

  • 出色的工作,我已经更新了教程(http://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift)以删除自动释放池.我会尽快给予奖励. (4认同)
  • 比我希望的更好!谢谢! (2认同)
  • 我还应该补充一点,不仅适用于`NSOperation`,还适用于`NSBlockOperation`和`NSInvocationOperation` (2认同)