使用NSAutoreleasePool从线程方法返回后,NSRunLoop出错

时间:2010-08-09 22:04:25

标签: objective-c multithreading nsautoreleasepool nsrunloop

从我已经设置了NSAutoreleasePool的线程方法返回后,我收到了EXC_BAD_ACCESS错误。失败的地方是致电NSRunLoop。我试图包装一个主要由一个类组成的第三方库(让我们称之为Connection类)及其委托,这样它将同步运行而不是异步运行到客户端类。包装类(称为NFCConnection)符合委托协议,并在NFCConnection的构造函数中传递给Connection的setDelegate方法。

我创建新线程的方法如下:

- (void)methodA {
    [NSThread detachNewThreadSelector:@selector(doSomethingImportant) 
                             toTarget:self 
                           withObject:nil];

    while (!callBackInvoked) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode     // Error!
                                 beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];        
    }
}

线程方法doSomethingImportant如下所示:

- (void)doSomethingImportant {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    
    [[Connection instance] somethingImportant];    
    [pool release];
}

对Connection的somethingImportant的调用将导致在主线程上调用其中一个委托方法。在每个回调方法中,我将变量callBackMethod设置为NO,导致methodA中的while循环退出。在doSomethingImportant返回之后但在调用其中一个委托方法之前发生错误。在调用NSRunLoop时发生EXC_BAD_ACCESS错误。这是堆栈跟踪的一部分:

#0  0x3138cec0 in objc_msgSend
#1  0x000100ac in -[Connection ProcessRx_Api_SomethingImportant:] at Connection.m:621
#2  0x000114fa in +[Connection ProcessRx:] at Connection.m:900
#3  0x00012758 in -[CommHandling ProcessRx:length:] at CommHandling.m:294
#4  0x000126b8 in -[CommHandling stream:handleEvent:] at CommHandling.m:331
#5  0x30a7b958 in -[EAInputStream _streamEventTrigger]
#6  0x30a7be78 in __streamEventTrigger
#7  0x323f53a6 in CFRunLoopRunSpecific
#8  0x323f4c1e in CFRunLoopRunInMode
#9  0x3373c966 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:]
#10 0x0000ae66 in -[NFCConnection methodA:] at NFCConnection.m:137
#11 0x0000bbf2 in -[NFCTestViewController doIt] at NFCTestViewController.m:39
...

现在我可以防止错误发生,如果我忽略在doSomethingImportant中设置自动释放池,则包装的API似乎同步工作。但如果我这样做,那么以下内容将在控制台中打印出来:

2010-08-09 14:54:49.259 ConnetionTest[3353:652f] *** _NSAutoreleaseNoPool(): Object 0x1928b0 of class __NSCFDate autoreleased with no pool in place - just leaking
Stack: (0x3374ff83 0x33723973 0x3372393f 0x323f78f1 0x3372b913 0x10221 0xc833 0xb045 0x33731acd 0x336dfd15 0x33ad8788)
2010-08-09 14:54:49.272 ConnetionTest[3353:652f] *** _NSAutoreleaseNoPool(): Object 0x18f800 of class NSCFTimer autoreleased with no pool in place - just leaking
Stack: (0x3374ff83 0x33723973 0x3372393f 0x3372b93b 0x10221 0xc833 0xb045 0x33731acd 0x336dfd15 0x33ad8788)

上面的这些消息是否是由Connection类实例中未发布的内容引起的?我正在创建一个NSDate但不是需要发布的实例。与NSTimer相同。

我试图通过设置NSAutoreleasePool来做正确的事情,但看起来我做错了。但是我不清楚这可能是什么。任何帮助非常感谢!

1 个答案:

答案 0 :(得分:1)

首先,你确实需要新线程有一个自动释放池,所以不要试图解决这个问题。 : - )

也就是说,这有一些过早发布或代码中过度释放的标志。我会特别注意Connection对象的委托,因为关于保留其委托的对象的规则有点滑。 (Cocoa对象一般保留他们的代表,但第三方代码可能 - 有时候有充分的理由。)

我现在请你注意Tracking Memory Usage。 MallocDebug和NSZombieEnabled = YES的一些排列应该最终揭开罪魁祸首代码。

但是,一旦你越过这个bug,你可能想要探索Grand Central Dispatch这种事情,而不是滚动你自己的线程...你的代码看起来应该是它应该使用NSCondition或pthread_condition变量严格正确。虽然你可以在当前的硬件上侥幸逃脱,但是这些不同步的共享访问很容易引起一些非常讨厌的竞争。 GCD(AKA libdispatch)提供了一个很多更清洁和更现代的范例,所以如果你要学习一些新的东西,那么很多比pthreads / NSThread /等更好的投资。 : - )