Альтернатива Grand Central Dispatch использованию NSTimer - многократное аннулирование

У меня проблема с dispatch_source_t, который я пытаюсь использовать. Я хочу использовать его для задержки обработки PHChange на 5 секунд, потому что PHChange может произойти несколько раз за короткий промежуток времени. Буду признателен за любую предложенную помощь. По сути, я хочу отменить предыдущий таймер dispatch_source_t почти как NSTimer.

@property (nonatomic, strong) dispatch_source_t libraryChangedTimer;

dispatch_source_t CreateTimerDispatchSource(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
{
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    if (timer)
    {
        dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);

        dispatch_source_set_event_handler(timer, block);

        dispatch_resume(timer);
    }

    return timer;
}

- (void)libraryChanged:(PHChange *)changeInstance
{
    NSLog(@"Called immediately and it shouldn't");
}

- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
    if (self.libraryChangedTimer)
    {
        dispatch_source_cancel(self.libraryChangedTimer);

        self.libraryChangedTimer = CreateTimerDispatchSource(5ull * NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            [self libraryChanged:changeInstance];
            dispatch_source_cancel(self.libraryChangedTimer);
        });
    }
    else
    {
        self.libraryChangedTimer = CreateTimerDispatchSource(5ull * NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            [self libraryChanged:changeInstance];
            dispatch_source_cancel(self.libraryChangedTimer);
        });
    }
}

person klcjr89    schedule 07.06.2015    source источник
comment
У меня есть встроенный класс таймера на основе GCD, который вы можете свободно использовать в своем приложении: github.com/mattneub/Programming-iOS-Book-Examples/blob/master/   -  person matt    schedule 08.06.2015
comment
dispatch_source_t не может быть strong или retain в Xcode 7.1.1. Ваш код не будет компилироваться.   -  person Cœur    schedule 11.11.2015
comment
@Cœur - Нет, это неправда. Если вы видите эту ошибку, возможно, это связано с тем, что вы установили флаг компилятора -DOS_OBJECT_USE_OBJC=0, который, как описано в <os/object.h>, отключает поведение объекта для типов GCD. Но я повторно протестировал это в Xcode 7.1.1 (а также в Xcode 7.2 beta 3), и strong работает нормально.   -  person Rob    schedule 11.11.2015
comment
Вы также получите эту ошибку, если вы ориентируетесь на версии OS X до 10.8 или версии iOS до 6, поскольку это предшествует обработке объектов типов GCD и, следовательно, требует ручного dispatch_retain и dispatch_release.   -  person Rob    schedule 11.11.2015
comment
Вы правы, я ориентировался на iOS5. Код подходит для iOS6.   -  person Cœur    schedule 11.11.2015


Ответы (2)


Ваша проблема в этой строке:

    dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);

По сути, вы указываете «сейчас» в качестве времени запуска таймера. Это то, что вычисляет dispatch_walltime(NULL, 0). Вы передаете свое значение interval в качестве интервала таймера, который просит его повторить с этим периодом между срабатываниями. Но время начала определяет время первого срабатывания.

То, что вы хотели, было:

    dispatch_source_set_timer(timer, dispatch_walltime(NULL, interval), interval, leeway);

Или, если вы на самом деле не хотите, чтобы таймер повторялся:

    dispatch_source_set_timer(timer, dispatch_walltime(NULL, interval), DISPATCH_TIME_FOREVER, leeway);
person Ken Thomases    schedule 07.06.2015

Я понял это довольно просто, используя код dispatch_after:

dispatch_source_t CreateTimerDispatchSource(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
{
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    if (timer)
    {
        dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), DISPATCH_TIME_FOREVER, leeway);

        dispatch_source_set_event_handler(timer, block);

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval), queue,^
        {
            dispatch_resume(timer);
        });
    }

    return timer;
}

- (void)libraryChanged:(PHChange *)changeInstance
{
    // Do something 
}

- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
    if (self.libraryChangedTimer)
    {
        dispatch_source_cancel(self.libraryChangedTimer);

        self.libraryChangedTimer = CreateTimerDispatchSource(5ull * NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            [self libraryChanged:changeInstance];
            dispatch_source_cancel(self.libraryChangedTimer);
            self.libraryChangedTimer = nil;
        });
    }
    else
    {
        self.libraryChangedTimer = CreateTimerDispatchSource(5ull * NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            [self libraryChanged:changeInstance];
            dispatch_source_cancel(self.libraryChangedTimer);
            self.libraryChangedTimer = nil;
        });
    }
}
person klcjr89    schedule 07.06.2015