4

I am trying to create a loop like this:

while (TRUE){
  dispatch_after(...{
    <some action>
  });
}

After a viewDidLoad. The idea is to repeat the dispatch_after repeatedly. The dispatch_after waits two seconds before doing the action.

This does not work - the screen just blanks? Is it stuck in looping or ...?

hfossli
  • 22,616
  • 10
  • 116
  • 130
Roel Van de Paar
  • 2,111
  • 1
  • 24
  • 38

4 Answers4

11

Yes, you can do that with gcd. You need two additional c-functions though.

static void dispatch_async_repeated_internal(dispatch_time_t firstPopTime, double intervalInSeconds, dispatch_queue_t queue, void(^work)(BOOL *stop)) {    
    __block BOOL shouldStop = NO;
    dispatch_time_t nextPopTime = dispatch_time(firstPopTime, (int64_t)(intervalInSeconds * NSEC_PER_SEC));
    dispatch_after(nextPopTime, queue, ^{
        work(&shouldStop);
        if(!shouldStop) {
            dispatch_async_repeated_internal(nextPopTime, intervalInSeconds, queue, work);
        }
    });
}

void dispatch_async_repeated(double intervalInSeconds, dispatch_queue_t queue, void(^work)(BOOL *stop)) {
    dispatch_time_t firstPopTime = dispatch_time(DISPATCH_TIME_NOW, intervalInSeconds * NSEC_PER_SEC);
    dispatch_async_repeated_internal(firstPopTime, intervalInSeconds, queue, work);
}

Tested! Works as intended.

https://gist.github.com/4676773

hfossli
  • 22,616
  • 10
  • 116
  • 130
7

The dispatch_after(...) call returns immediately no matter when it is scheduled to run. This means that your loop is not waiting two seconds between dispatching them. Instead you are building an infinite queue of things that will happen two seconds from now, not two seconds between each other.

So yes, you are stuck in an infinite loop of adding more and more blocks to be executed. If you want something to happen every two second then you could use a repeating NSTimer or have the block dispatch_after inside itself (so that the second block runs two seconds after the first).

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • David, do you have an example of using [NSTimer (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nstimer_Class/Reference/NSTimer.html)] in the way that I want to use it? I guess the while loop could remain in place? Something like + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds invocation:(NSInvocation *)invocation repeats:(BOOL)repeats maybe? – Roel Van de Paar Jul 13 '12 at 06:23
  • 1
    You wouldn't need the loop if you have a repeating timer. The timer will fire every two seconds by itself and only needs to be started once. – David Rönnqvist Jul 13 '12 at 06:28
  • so but where does the NSTimer go - can I still put it in viewDidLoad? – Roel Van de Paar Jul 13 '12 at 06:34
  • Yes, you can still put it in viewDidLoad – David Rönnqvist Jul 13 '12 at 07:03
  • Ok, got it to work now. Used a scheduledTimerWithTimeInterval NSTimer in viewDidLoad and called a timer function? selector:@selector(onTimer:) from it. – Roel Van de Paar Jul 13 '12 at 09:01
6

GCD already got this built in

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10);
    dispatch_source_set_event_handler(timer, block);
    dispatch_resume(timer);
}

https://gist.github.com/maicki/7622108

hfossli
  • 22,616
  • 10
  • 116
  • 130
  • 1
    Imho this is the best answer. And `dispatch_source_cancel()` can be used to cancel the timer. Details - http://stackoverflow.com/a/14591070/2151614 – Zmey Aug 04 '16 at 13:02
1

If you'd like an async task to run after a delay to check for example if a tag has been updated, then finish, you could use the code below:

typedef void (^RepeatCompletionHandler)(BOOL isRepeat);

typedef void (^RepeatBlock)(RepeatCompletionHandler completionHandler);

- (void)dispatchRepeat:(int)seconds withBlock:(RepeatBlock)block {

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC),
                   dispatch_get_main_queue(), ^() {
                       block(^(BOOL isRepeat) {
                           if (isRepeat) {
                               return [self dispatchRepeat:seconds withBlock:block];
                           }
                       });
                   });

}

For example:

[self dispatchRepeat:5 withBlock:^(RepeatCompletionHandler completionHandler) {

    [tagsService getTagValueForTagName:TagName value:^(NSString *tagValue) {
        if (![TagValue isEqualToString:tagValue]) {
            return completionHandler(YES);
        }
        completionHandler(NO);
    }];

}];
Pellet
  • 2,254
  • 1
  • 28
  • 20