【问题标题】:Is @autoreleasepool still required for modern iOS 8 NSOperation usage?现代 iOS 8 NSOperation 使用仍然需要 @autoreleasepool 吗?
【发布时间】:2014-07-03 19:38:35
【问题描述】:

我已经阅读了Concurrency Programming Guide

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

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

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

@implementation ImageDownloader

- (void)main {
    @autoreleasepool {
      ...
    }
}     
@end
  1. 我应该如何实现现代 NSOperation 对象?
  2. Apple 指的是 2012-07-17 的更新是什么?

【问题讨论】:

  • 自动释放池几乎无用的地方是在您的主程序周围。在那里,它仅用于消除“刚刚泄漏”的警告消息,而无需实际执行任何操作。

标签: ios automatic-ref-counting nsoperation ios8 nsautoreleasepool


【解决方案1】:

如果您从NSOperation 派生并实现main 方法,则无需设置自动释放池。 start 方法的默认实现推送NSAutoReleasePool,调用main,然后弹出NSAutoReleasePoolNSInvocationOperationNSBlockOperation 也是如此,它们共享 start 方法的相同实现。

以下是NSOperationstart 方法的精简反汇编。注意对 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

这是一些正在运行的示例代码的快照

  1. MyObj 是在 main 方法中分配的,我确保该对象必须自动释放
  2. main 返回到_start,下图显示了一个堆栈跟踪,其中MyObj dealloc 被当前自动释放池调用,弹出在_start

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

#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;
}

Grand Central Dispatch 同样根据Concurrency Programming Guide 管理调度队列的自动释放池:

如果您的代码块创建了多个 Objective-C 对象,您可能希望将代码块的部分代码包含在 @autorelease 代码块中,以处理这些对象的内存管理。尽管 GCD 调度队列有自己的自动释放池,但它们不能保证这些池何时耗尽。如果您的应用程序受到内存限制,创建您自己的自动释放池可以让您更定期地为自动释放的对象释放内存。

【讨论】:

  • 比我希望的要好!谢谢!
  • 我还应该补充一点,这不仅适用于NSOperation,也适用于NSBlockOperationNSInvocationOperation
  • 出色的工作,我更新了教程 (raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift) 以删除自动释放池。我会尽快奖励赏金。
  • 附带说明,您使用的是什么主题?看起来很棒!
  • 我真的很喜欢这个详细的答案+1!
猜你喜欢
  • 1970-01-01
  • 2012-04-15
  • 2012-01-14
  • 2015-09-06
  • 2021-05-02
  • 1970-01-01
  • 1970-01-01
  • 2015-05-21
  • 1970-01-01
相关资源
最近更新 更多