【问题标题】:Is it safe to autorelease objects inside an NSOperation?在 NSOperation 中自动释放对象是否安全?
【发布时间】:2011-01-18 13:56:01
【问题描述】:

我在 NSOperationQueue 中运行 NSInvocationOperation 类型的操作,并且想知道自动释放对象是否安全 - 也就是说,是否保证为每个操作启动的线程都有自己的自动释放池。

我没有找到任何用于操作的文档自动释放池 - 阅读 Apple 的文档实际上表明我确实需要定义自己的自动释放池。

但是: 1) 我看不到仪器中的任何泄漏,至少不会超过我在操作中分配自己的自动释放池时。

2) 在调试器中我可以看到这个堆栈跟踪:

#0  0x00fc3e82 in -[NSObject(NSObject) release] ()
#1  0x00faaa6c in CFRelease ()
#2  0x00fbf804 in __CFBasicHashDrain ()
#3  0x00faabcb in _CFRelease ()
#4  0x00fcfb8d in _CFAutoreleasePoolPop ()
#5  0x000edd0d in -[__NSOperationInternal start] ()
#6  0x000ed826 in ____startOperations_block_invoke_2 ()
#7  0x94358024 in _dispatch_call_block_and_release ()
#8  0x9434a2f2 in _dispatch_worker_thread2 ()
#9  0x94349d81 in _pthread_wqthread ()
#10 0x94349bc6 in start_wqthread ()

所以看起来好像有一个 CFAutoreleasePool - 假设这个对象会在操作完成后对我所有的自动释放对象调用 release 是否安全?

【问题讨论】:

    标签: cocoa nsoperation autorelease


    【解决方案1】:

    我写了一个小程序来测试NSInvocationOperation是否会为这个操作创建一个自动释放池:

    #import <Foundation/Foundation.h>
    
    @interface MyClass : NSObject
    @end
    
    @implementation MyClass
    - (void)performSomeTask:(id)data
    {
        NSString *s = [[[NSString alloc] initWithFormat:@"hey %@", data]
            autorelease];
        if ([[NSThread currentThread] isMainThread])
            NSLog(@"performSomeTask on the main thread!");
        else
            NSLog(@"performSomeTask NOT on the main thread!");
    
        NSLog(@"-- %@", s);
    }
    @end
    
    int main(int argc, char *argv[]) {
      MyClass *c = [MyClass new];
    
      if (argc == 2 && strcmp(argv[1], "nop") == 0)
          [c performSomeTask:@"ho"];
      else {
          NSInvocationOperation *op = [[NSInvocationOperation alloc]
              initWithTarget:c
                    selector:@selector(performSomeTask:)
                      object:@"howdy"];
          NSOperationQueue *queue  = [[NSOperationQueue alloc] init];
          [queue addOperation:op];
          [op waitUntilFinished];
    
          [op release];
          [queue release];
      }
    
      [c release];
    
      return 0;
    }
    

    它的工作原理如下:如果在命令行中传递“nop”,它将直接在主线程上执行-performSomeTask:,没有自动释放池。结果输出为:

    $ ./c nop
    *** __NSAutoreleaseNoPool(): Object 0x10010cca0 of class NSCFString autoreleased with no pool in place - just leaking
    performSomeTask on the main thread!
    -- hey ho
    

    -performSomeTask: 中的自动释放字符串导致泄漏。

    在不传递“nop”的情况下运行程序将通过NSInvocationOperation 在不同的线程上执行-performSomeTask:。结果输出为:

    $ ./c
    *** __NSAutoreleaseNoPool(): Object 0x100105ec0 of class NSInvocation autoreleased with no pool in place - just leaking
    *** __NSAutoreleaseNoPool(): Object 0x100111300 of class NSCFSet autoreleased with no pool in place - just leaking
    *** __NSAutoreleaseNoPool(): Object 0x100111b60 of class NSCFSet autoreleased with no pool in place - just leaking
    *** __NSAutoreleaseNoPool(): Object 0x100105660 of class NSCFSet autoreleased with no pool in place - just leaking
    performSomeTask NOT on the main thread!
    -- hey howdy
    

    正如我们所见,NSInvocationNSSet 的实例正在泄漏-performSomeTask: 中的自动释放字符串没有泄漏,因此为此创建了一个自动释放池调用操作。

    我认为可以安全地假设 NSInvocationOperation(可能还有 Apple 框架中的所有 NSOperation 子类)创建自己的自动释放池,就像并发编程指南建议的自定义 NSOperation 子类一样。

    【讨论】:

    • 你在 iOS 3.x 上测试过这个吗?这可能是此处描述的 GCD 实现细节:stackoverflow.com/questions/4141123/…
    • @Danra AFAICT,NSOperationQueue 不使用 GCD。另外,如果您将上面的代码更改为通过[op start] 同步运行,则不会有消息显示字符串正在泄漏,这意味着NSInvocationOperation 为该操作创建了一个自动释放池。
    • @Danra 我只在 Mac OS 10.6.6 上测试过这种行为。
    • 我已经提交了一份雷达文件,要求文档明确声明 Foundation NSOperation 子类创建自己的自动释放池。
    • 我假设使用了 GCD,因为我在堆栈跟踪中看到了对 _dispatch_call_block_and_release 的调用。感谢您的雷达,如果您得到官方答复,请更新此线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 2011-07-01
    • 1970-01-01
    • 2011-11-18
    • 2013-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多