20

我试图取消然后释放一个暂停的计时器,但是当我在它上面调用'dispatch_release'时,我立即得到EXC_BAD_INSTRUCTION。

这不是对计时器采取的一组有效操作吗?

定时器创建和暂停:

@interface SomeClass: NSObject { }
@property (nonatomic, assign) dispatch_source_t             timer;
@end

// Class implementation
@implementation SomeClass

@synthesize timer = _timer;

- (void)startTimer 
{
    dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 
                                    0, 0, globalQ); 

    dispatch_time_t startWhen = dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1);
    dispatch_source_set_timer(_timer, startWhen, 1 * NSEC_PER_SEC, 5000ull);

    dispatch_source_set_event_handler(_timer, ^{
        // Perform a task 

        // If a particular amount of time has elapsed, kill this timer
        if (timeConstraintReached)
        {
            // Can I suspend this timer within it's own event handler block?
            dispatch_suspend(_timer);
        }
    });

    dispatch_resume(_timer);
}

- (void)resetTimer
{
    dispatch_suspend(_timer);

    dispatch_source_cancel(_timer);

    // dispatch_release causes 
    // 'EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    dispatch_release(_timer);

    self.timer = nil;    
}
@end

此外,我可以在计时器源的 event_handler 块中调用 dispatch_suspend 吗?

任何帮助,将不胜感激。

4

1 回答 1

31

它崩溃的原因是因为这段代码

void
_dispatch_source_xref_release(dispatch_source_t ds)
{
    if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) {
        // Arguments for and against this assert are within 6705399
        DISPATCH_CLIENT_CRASH("Release of a suspended object");
    }
    _dispatch_wakeup(ds);
    _dispatch_release(ds);
}

所以,你不能释放一个dispatch_source_t已经被暂停的。resetTimer我猜你可能只想不暂停它。

虽然我在文档中找不到任何关于他们为什么会这样写的内容(并且评论暗示了我们永远不会看到的雷达中的利弊),但我所能做的就是参考它所在的文档说

您可以使用 dispatch_suspend 和 dispatch_resume 方法临时暂停和恢复调度源事件的传递。这些方法增加和减少调度对象的挂起计数。因此,您必须在事件传递恢复之前平衡对 dispatch_suspend 的每次调用与对 dispatch_resume 的匹配调用。

虽然这并不是说你不能释放一个被暂停的调度源,但它确实说你必须平衡每个调用,所以我假设它使用了一个调度信号量,它有待平衡后才可释放。这只是我的猜测:-)。

至于“我可以在计时器源的 event_handler 块中调用 dispatch_suspend 吗”。我很确定你可以,是的,根据以下文档dispatch_suspend

在调用时运行的任何块完成后发生暂停。

于 2012-03-05T19:00:44.450 回答