dispatch_source_cancel в приостановленном таймере вызывает EXC_BAD_INSTRUCTION - PullRequest
18 голосов
/ 05 марта 2012

Я пытаюсь отменить и затем освободить приостановленный таймер, но когда я вызываю '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

Кроме того, могу ли я вызвать dispatch_suspend в блоке event_handler источника таймера?

Любая помощь будет оценена.

1 Ответ

30 голосов
/ 05 марта 2012

Причина сбоя в том, что этот код :

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 перед событием Доставка возобновляется.

Хотя это не говорит о том, что вы не можете освободить источник отправки, который был приостановлен, оно говорит о том, что вы должны балансировать каждый вызов, поэтому я предполагаю, что это что-то вроде того, что он использует семафор диспетчеризации под капюшон, который должен быть сбалансирован, прежде чем он может быть освобожден Хотя это только мое предположение: -).

Что касается «могу ли я вызвать dispatch_suspend в блоке event_handler источника таймера». Я уверен, что вы можете, да, согласно документам для dispatch_suspend:

Приостановка происходит после завершения любых блоков, работающих во время вызова.

...