My requirement is that I want to call an API to ask for some new information from my server every 6 seconds,so I wrote my code as below:
MyBackgroundThread(){
while(self.isStop){
[self callMyAPI];
[NSThread sleepfortimeinterval : 6 ];
}
}
But I find out today that there is a way provided by Foundation library to write a run loop. So I can rewrite my code as below:
MyBackgroundThread(){
NSTimer *timer = [NSTimer timerWithTimeInterval:6 target:self selector:@selector(callMyAPI) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
while (! self.isCancelled) {
BOOL ret = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
However, I don't know if these is a better way to do my job then my original one? If it is, why? and how can I test the difference in efficiency(or other property?) between this two ways?
Thanks!
I think it's generally unnecessary to create new run loop for timer. I'd suggest one of two approaches:
Schedule NSTimer
on main run loop, but have the called method then dispatch the request to background queue.
Create dispatch timer scheduled to run on designated background dispatch queue. To do that, create dispatch timer property:
@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);
There are times that creating a new run loop is useful, but it seems unnecessary in this simple scenario.
Having said that, it probably doesn't make sense to use a timer for initiating a network every six seconds. Instead, you probably want to start the next request six seconds after the prior one finishes. For a variety of reasons, your server might not be able to respond within six seconds, and you don't want concurrent requests to build up in these scenarios (which can happen if your requests run asynchronously).
So, I'd be inclined that the completion block of callMyAPI
to do something as simple as:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), queue, ^{
<#code to issue next request#>
});
This obviates the need for timers (and custom run loops) entirely.
Finally, if you really need to detect system changes with that frequency, it might suggest a very different server architecture. For example, if you're polling every six seconds to see if something changed on the server, you might consider a sockets-based implementation or use push notifications. In both of those approaches, the server will tell the client apps when the significant event takes place, rather than the app behaving like Bart Simpson in the back seat of the car, constantly asking "are we there yet?"
The appropriate architecture is probably a function of with what frequency the server data is likely to be changing and what the client app requirements are.