0

从设置了 NSAutoreleasePool 的线程方法返回后,我收到 EXC_BAD_ACCESS 错误。失败的地方是调用 NSRunLoop。我正在尝试包装一个主要由一个类(我们称其为 Connection 类)及其委托组成的第 3 方库,以便它与客户端类同步而不是异步运行。名为 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,导致方法 A 中的 while 循环退出。该错误发生在 doSomethingImportant 返回之后但在调用其中一个委托方法之前。EXC_BAD_ACCESS 错误发生在调用 NSRunLoop 时。这是堆栈跟踪的一部分:

#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 来做正确的事情,但看起来我做错了什么。但是,我不知道那可能是什么。非常感谢任何帮助!

富有的

4

1 回答 1

1

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

也就是说,这具有过早发布或代码中某处过度发布的一些特征。我会特别注意 Connection 对象的委托,因为关于保留其委托的对象的规则有些模糊。(Cocoa 对象通常保留它们的委托,但第三方代码可能 - 有时是有充分理由的。)

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

但是,一旦您克服了这个错误,您可能想要探索 Grand Central Dispatch 来处理这类事情,而不是滚动您自己的线程……您的代码看起来应该使用 NSCondition 或 pthread_condition 变量来严格正确。虽然您可以在当前硬件上逃脱很多,但这些类型的不同步共享访问很容易导致一些非常讨厌的比赛。GCD (AKA libdispatch) 提供了一个干净、更现代的范例,所以如果你要学习新的东西,它比 pthreads / NSThread / 等投资要好得多。:-)

于 2010-08-09T22:57:41.907 回答