Run repeating NSTimer with GCD?

Shredder2794 picture Shredder2794 · May 9, 2012 · Viewed 15k times · Source

I was wondering why when you create a repeating timer in a GCD block it doesen't work?

This works fine:

-(void)viewDidLoad{
    [super viewDidLoad];
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
}
-(void)runTimer{
    NSLog(@"hi");
}

But this doesent work:

dispatch_queue_t myQueue;

-(void)viewDidLoad{
    [super viewDidLoad];

    myQueue = dispatch_queue_create("someDescription", NULL);
    dispatch_async(myQueue, ^{
        [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
    });
}
-(void)runTimer{
    NSLog(@"hi");
}

Answer

Lily Ballard picture Lily Ballard · May 9, 2012

NSTimers are scheduled on the current thread's run loop. However, GCD dispatch threads don't have run loops, so scheduling timers in a GCD block isn't going to do anything.

There's three reasonable alternatives:

  1. Figure out what run loop you want to schedule the timer on, and explicitly do so. Use +[NSTimer timerWithTimeInterval:target:selector:userInfo:repeats:] to create the timer and then -[NSRunLoop addTimer:forMode:] to actually schedule it on the run loop you want to use. This requires having a handle on the run loop in question, but you may just use +[NSRunLoop mainRunLoop] if you want to do it on the main thread.
  2. Switch over to using a timer-based dispatch source. This implements a timer in a GCD-aware mechanism, that will run a block at the interval you want on the queue of your choice.
  3. Explicitly dispatch_async() back to the main queue before creating the timer. This is equivalent to option #1 using the main run loop (since it will also create the timer on the main thread).

Of course, the real question here is, why are you creating a timer from a GCD queue to begin with?