暂停计时器上的dispatch_source_cancel会导致EXC_BAD_INSTRUCTION

时间:2012-03-05 18:41:22

标签: iphone ios ios5 grand-central-dispatch dispatchertimer

我正在尝试取消然后释放暂停的计时器但是当我调用'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吗?

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:30)

崩溃的原因是this code

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中暂停它。

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

  

您可以暂停和恢复调度源事件的传递   暂时使用dispatch_suspend和dispatch_resume方法。   这些方法增加和减少你的暂停计数   派遣对象。因此,您必须平衡每次通话   dispatch_suspend,在事件发生之前匹配对dispatch_resume的调用   送货恢复。

虽然这并没有说你不能发布一个被暂停的调度源,但是它确实说你必须平衡每个调用,所以我假设它是使用一个调度信号量下面的东西。引擎盖必须是balanced before they can be released。这只是我的猜测: - )。

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

  

在完成呼叫时运行的任何阻止后,将暂停。