【问题标题】:NSThread object retained twice?NSThread 对象保留了两次?
【发布时间】:2009-12-08 17:25:06
【问题描述】:

我有一个派生自 NSThread 的类:

@interface FSEventMonitorThread : NSThread {
    FSEventStreamRef m_fseStreamRef;
    CFRunLoopRef m_runLoop;
}

- (id) initWithStream:
    (FSEventStreamRef)fseStreamRef;

- (void) dealloc;

- (void) main;

@end

@implementation FSEventMonitorThread

- (id) initWithStream:
    (FSEventStreamRef)fseStreamRef
{
    if ( self = [super init] )
        m_fseStreamRef = fseStreamRef;
    return self;
}

 - (void) dealloc
{
    CFRunLoopStop( m_runLoop );
    FSEventStreamStop( m_fseStreamRef );
    [super dealloc];
}

- (void) main
{
    m_runLoop = CFRunLoopGetCurrent();
    FSEventStreamScheduleWithRunLoop(
        m_fseStreamRef, m_runLoop, kCFRunLoopDefaultMode
    );
    FSEventStreamStart( m_fseStreamRef );
    CFRunLoopRun();
}

@end

在其他地方(在 C++ 函数内),我创建了一个实例:

m_thread = [[FSEventMonitorThread alloc] initWithStream:m_fseStreamRef];

我的理解是,retain-count 现在应该是 1。 在另一个 C++ 函数中,我想停止并释放线程:

[m_thread release];

然而dealloc 方法没有被调用。如果我改为这样做:

[m_thread release];
[m_thread release];

然后 dealloc 被调用,这意味着保留计数为 2。但它是如何变为 2 的?

请注意,NSThread 的文档仅提及使用 detachNewThreadSelector:toTarget:withObject: 时的保留。

【问题讨论】:

    标签: objective-c cocoa release nsthread retain


    【解决方案1】:

    框架本身保留线程的所有权。这是必要的,以便线程对象在 main 方法执行时不会消失。如果你想停止一个线程,你做错了。您必须提供某种线程间通信来向线程的 main 方法发出信号,表明它应该停止正在执行的任何操作、清理并退出。一旦发生这种情况,放弃您对线程的所有权将导致线程释放。您永远不应该简单地过度释放某些东西以使其“消失”。如果你这样做了,你几乎肯定不会像本例那样使用提供的对象。

    取消线程的一个非常简单的示例可能是:

    - (void)finishThread
    {
      if( [NSThread currentThread] != self ) // dispatch this message to ourself
        [self performSelector:@selector(finishThread) onThread:self withObject:nil waitUntilDone:NO];
      else
        CFRunLoopStop(CFRunLoopGetCurrent());
    }
    

    【讨论】:

    • CFRunLoopRun() 在调用 CFRunLoopStop() 之前不会返回;因此,我的 main() 无法检查 isCancelled。我重写了取消方法来调用 CFRunLoopStop()。所以现在要停止线程,我正在执行 [m_thread cancel],然后是 [m_thread release],这样就可以了。问题:重写取消方法是否可行?文档并没有说不应该。
    • 这取决于你在做什么,但总的来说,我会说不,不要那样做。最简单的方法是添加一个像 finishThread 这样的方法;我将在我的答案中添加一个快速示例。您还应该在 CFRunLoopRun() 函数返回后进行任何需要的清理工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 2014-12-09
    相关资源
    最近更新 更多