I am new to iOS development (I'm most familiar with Java), and I was wondering what is the best method for starting a long running thread? This thread is going to start whenever the application is in the foreground and will stop when it goes to the background. While it is in the foreground, it is going to poll an external device every X seconds to see if if it connected.
NSThread is very similar to the Java thread class, which makes it very easy for me to understand. I know that I can just initWithTarget:selector:object:
that class and call start:
and it'll start up the thread with the method supplied in the selector. In there I just have a while(true)
loop that keeps on running until I break out of it. Here is a basic example of what I'm looking to do:
- (void)startup {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(devicePoller) object:nil];
[thread start];
}
- (void)devicePoller {
while (self.started) {
if (![self.device isConnected]) {
//notify the user the device isn't connected
}
[NSThread sleepForTimeInterval:2];
}
}
However, I am seeing that people recommend using GCD because it has better performance. I understand how to use dispatch_async()
for executing code asynchronously, but all of the examples out there seem to be for a single long running operation, not an operation that runs the entire time the application is running. Is there a way to do this with GCD (and should I) or something else?
If you needed to poll and you want to do it on a background thread, I might suggest a dispatch timer:
and you can then configure this timer to fire every two seconds:
See the discussion of dispatch sources in the Concurrency Programming Guide. Also see the Grand Central Dispatch (GCD) Reference.
But most devices support some form of event-driven notification that obviates the need for any polling like this, but if you have to poll, and if you want to do it off the main thread, this is one way to do it.
The dispatch timer is useful when you want a timer to run on a background queue. If your vendor's API is responsive enough to run seamlessly in the main queue, then use
NSTimer
approach. It keeps you from having to do a lot of extra work to make your code thread-safe (e.g. proper synchronization as you update model objects, etc.). I assumed from your original question's thread-based approach that you had some reason to not use a timer and you had to move it to another thread for some reason, in which case I contend that a dispatch timer might be a better way to handle it than doingNSThread
programming with a perpetualwhile
loop. But if you (a) have to poll; and (b) don't have a compelling need to write multi-threaded code, then your life may be simpler if you usedNSTimer
-based approach.Personally, I'd make sure I exhausted the Core Bluetooth approach before I pursued timers. App-level polling of a physical device should be considered the approach of last resort. Perhaps you can contact the vendor of the API and see if they have suggestions other than polling (because if they've been doing this for a while, they might have more elegant solutions that they can suggest).
Regarding receiving web service updates, again polling is horribly inefficient (and depending upon frequency of your polling, it can adversely affect battery life, consume cellular data plan, etc.). Push notifications might be the way to go if the server data is changing infrequently, but you want your app to be proactively notified of changes. Or if the server is really changing constantly, then maybe some socket based approach might make sense. But polling a network resource every two seconds (if that's what you meant to suggest) is rarely the right approach.
Just like polling of a physical device is the architecture of last resort, this is even more true for network communications. You really want to come up with an architecture that balances to device considerations with the business need. And if you conclude you have to do a polling-based approach, perhaps you might consider employing different polling frequency depending upon whether the user is on wifi vs. cellular.
The difference between NSThread/NSTimer and GCD operations/timers is simple:
NSThread/NSTimer are ~25 years old and behave the way anybody in the last 25 years would expect them to behave.
GCD is a cutting edge state of the art technology to hit extreme levels of performance and low battery consumption, in ways that simply cannot be achieved using traditional methods.
NSThread and NSTimer should generally be avoided these days. They are worse than GCD in every way except backwards compatibility and familiarity.
Use NSOperationQueue if you're looking for a high level API for GCD.
Note that GCD is using threads under the hood. But it uses them intelligently, taking into account information that the kernel knows about the hardware also other software running on the device, neither of which you can do in your own app. This delivers significantly better performance than you could ever hope to achieve using NSThread.
According to a blog post from four years ago (which is almost certainly outdated, and is executing on completely different hardware to a modern iOS device), a 6 second operation using NSThread takes 0.05 seconds using GCD. They both use threads, but GCD uses them more intelligently.
There is no reason to use either a thread or a GCD queue. Simply use an
NSTimer
ordispatch_after()
to periodically check to see if the device is still connected.However, this design is fundamentally counter to the recommended patterns. You should never poll unless you absolutely have to.
What kind of external device are you monitoring? Is there really no notification of any type sent when the device disconnects?
Typically, you check for connection viability at app launch and whenever your app comes to the foreground. During normal app operations, you would watch for whatever notification is sent whenever the connectivity state changes.