4

I have a timer triggered in 5 seconds added to global queue, though i am invalidating it after 2 seconds run loop won't terminate till 5 seconds. In the following snippet backgroundTimer is an instance var, and run is a member function. What's wrong in the following code which is blocking the run loop termination.

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    _backgroundTimer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(run) userInfo:nil repeats:NO];
    [ [NSRunLoop currentRunLoop] addTimer:_backgroundTimer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run];

    NSLog(@"Run loop terminated");

});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [_backgroundTimer invalidate];
    _backgroundTimer=nil;

});
pradeepa
  • 4,104
  • 5
  • 31
  • 41

2 Answers2

5

The first problem here is that your timer is being added to a run loop on some arbitrary background thread. (i.e. the thread will have been created by GCD to service the background queue) You can do this, but it kind of makes no sense.

That aside, you said that what you want to have happen is for this run loop to exit, but in the documentation for the -run method, it says the following:

Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. OS X can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop.

You will need to spin the event loop yourself if you want to exit it when the timer is invalid. For example:

while (_backgroundTimer.valid && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]);

This will exit the run loop a maximum of 0.1 (- epsilon) seconds after the timer is invalidated.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
0

Try like this:

[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:@selector(run) object:nil];

Edited:

-(void)run{
   if(taskComplete){
      [_backgroundtimer invalidate];   
}
cyberlobe
  • 1,783
  • 1
  • 18
  • 30
  • tried it didn't work, i think cancelPreviousPerformRequestsWithTarget will cancel only those which are scheduled using performSelector method family. – pradeepa Dec 16 '14 at 09:03
  • i need to exit from runloop before it calls run, not during 1st iteration to run method but during subsequent iteration. – pradeepa Dec 16 '14 at 09:15
  • @pradeepa your code works fine have you checked it properly ? It exits timer before run method calls – cyberlobe Dec 16 '14 at 09:33
  • it does not exit after 2 seconds it exits after 5 – pradeepa Dec 16 '14 at 09:34
  • @pradeepa do you mean it calls run method ? – cyberlobe Dec 16 '14 at 09:35
  • @pradeepa can you please add more description what exactly you require to achieve, May be we could find more simplistic way – cyberlobe Dec 16 '14 at 09:44
  • I need to terminate connections to server some time after the app reaches background, so i am using the runloop to check the remaining background time. when i decide its time to terminate the connection am calling invalidate. Also i am invalidating it when app becomes active. But invalidating timer is not terminating the run loop. – pradeepa Dec 16 '14 at 22:12