I have just created a background task with a timer using NSRunLoop
and NSTimer
in my ViewController
:
- (void)runBackgroundTask: (int) time{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTrackingBg) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
To call a function that will verify token validity, etc. Is it possible to end this loop from inside the function? For instance:
-(void)startTrackingBg
{
if(TOKEN IS NOT VALID)
{
STOP_THREAD;
dispatch_sync(dispatch_get_main_queue(), ^{
[self alertStatus:@"Session Lost!" :@"Error!"];
[self popToLogin];
});
}
}
A couple of thoughts:
If you look at the documentation for run
, they show a pattern that solves your problem:
If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the
NSDefaultRunLoopMode
by repeatedly invokingrunMode:beforeDate:
. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.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. A simple example would be:
BOOL shouldKeepRunning = YES; // global NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
where
shouldKeepRunning
is set toNO
somewhere else in the program.
Having said that, if I were going to start another thread, I wouldn't use up one of the global worker threads, but rather I'd just instantiate my own NSThread
.
More critically, depending upon what you're trying to do in this other thread, there are generally much better other patterns than establishing your own run loop.
For example, if I wanted to have timer run something in another queue, I'd use a dispatch timer instead:
@property (nonatomic, strong) dispatch_source_t timer;
and then instantiate and start dispatch timer source to run on your designated GCD queue:
dispatch_queue_t queue = dispatch_queue_create("com.domain.app.polltimer", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), kPollFrequencySeconds * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
<#code to be run upon timer event#>
});
dispatch_resume(self.timer);
Or, if you want to use NSTimer
, just schedule that on the main runloop, and have the method it calls dispatch the time consuming task to the background queue at that time. But, either way, I'd avoid adding the overhead of a second run loop.
Having shown you better ways to use timers in background threads, now that you describe the intent (polling a server) I'd actually recommend against using timer at all. Timers are useful when you want some action to be initiated at some regular interval. But in this case, you probably want to initiate the next server request after a certain amount of time after the previous request finished. So, in the completion block of the previous request, you might do something like:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20.0 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
<#code to initiate next request#>
});
Also, I'd personally want to make sure there was a very compelling reason to polling your server. Polling always seems so intuitively appealing and logical, but it is an extravagant use of the user's battery, CPU and data plan. And in those cases where you need the client to respond to server changes quickly, there are often better architectures (sockets, push notifications, etc.).