Concurrency in iOS
So… where to begin? There are many situations in which an application is required to do multiple things concurrently in order to improve the user experience, especially on iOS where the main thread controls the UI. This of course means that if we do anything too intensive on the main thread, the UI gets blocked and gets a bit jumpy… not good!
What is concurrency? #
At a very basic level, a processor can essentially do one thing. It can perform one command, followed by another, and another and so on, this is its life. Of course, with modern devices it’s a bit more complicated, but the basic principle still holds.
This system works fine when you have one piece of software running, but when there are multiple, say the OS and several other apps, all running at the same time, we need some way of sharing the processor time between the different processes. The OS basically loops through all of the active processes and allows each of them to execute a few commands on the processor in turn. So after your touch handling code has been executed, part of the OS might handle an app update, then another app gets a location update in the background, and then back to whatever your app needs to do next.
How do we get concurrency within our apps? This is where we introduce threads! An app can ‘own’ multiple threads. Now, threads work in much the same way as the different processes did in the last paragraph, they all get a share of the processor time. Every app or process gets a main thread by default, this is where the vast majority of your code probably runs. However, an app can have multiple threads, all running their own serial chain of commands concurrently (Ahhhhh!!!).
Let’s try an example… In iOS the main thread is where the UI is updated. Suppose I have a function that takes a really, really big image, adds one to the red channel of all its pixels and then returns a new image. And perhaps this takes several seconds. So I have an app that lets the user select an image, passes it to this function and pops the new image on the screen. Perfect! Except now I want to display a live count of how long this is taking to the user… in tenths of seconds! I add a timer to my app, which fires every tenth of a second, and sets the text on a visible label to the duration that has passed. Hmmm… the label seems to stay at 0 until the image is done, and then update to the total time taken.
Let’s look at why this is. So the user selects an image and the first thing we do is update the label to 0 seconds. This goes on the queue of things that our thread wants to do. Next we pass the image to our function, which goes onto the queue too. Then in a tenth of a second, and every tenth of a second thereafter we update the label, all of these calls, to update the label, get popped on the end of the queue. You might be starting to see where this is going now. A thread is serial, that is, everything that gets queued on it will get executed in order. When one thing finishes, the next will begin. This is a problem for us as we want the label to be updated while we process the image.
How do we solve this? Threads of course! While a single thread is serial, multiple threads run along side each other. So the processor may allow thread one to do its first thing, then thread two, then back to thread one for its second thing. This sounds handy, right? We have two things we want to happen at the same time, updating our label, and processing our image. Simples, we do one of them on another thread. In theory we could update the label on a separate thread, but in iOS we are limited by the fact that UIKit is not thread safe and we cannot update our UI if we are not on the main thread. So we get another thread to process the image. So now, thread 1 queues a call to set the label to 0 seconds. Then thread 2 queues the processing of the image (ta-da!), then a tenth of a second later we update the label, which we queue on thread 1. Now in super slow motion! The call to process the image is of course thousands of separate commands on the processor. The OS goes back to its loop. Finds thread one, and actions the first thing on its queue, and the label gets updated. Then it gets to thread 2 and lets it run a few commands required to process the image, but not the whole image. Then it gets back to thread 1, which now has another label update queued, so the OS lets it update the label. Then the loop continues, back to thread 2 to do the next few image processing commands, and back to thread 1 to update the label. And on it goes until the image is complete. At this point thread 2 gets terminated and the OS is only looping on thread 1.
In reality it is of course much more complicated than this, we have a processor, or now often multiple processors, graphical processing units, disk IO, memory access, and god knows what other pieces of hardware to be shared between the processes. But in principle this is roughly how it works. Sorry about there being no code, but this post is purely a theoretical concept post to try and get the idea straight.
Concurrency in iOS #
When writing apps for iOS Apple have given us multiple ways of accessing the underlying threading model. I’ll give a very brief overview of the main ones below:
- POSIX threads - This is a very powerful c based api for managing threads. It gives you direct access to the threads and has the benefit that, if you’re careful, it is cross platform compatible. Maybe a good choice if you’re writing library for doing some processor intensive things that you may later want to deploy to a different platform. Yolinux have a very nice overview tutorial on using the pthread library. It’s not pretty but it’s well explained. Of course you can always just google for pthread tutorials if it’s not to your liking.
- NSThread - A very pretty, cocoa, threading library provided by Apple. I’d suggest checking out their docs. You can create an NSThread with a target and selector and then call to start the thread. This will then execute the implementation pointed to by the target-selector pair on the new thread.
- Grand Central Dispatch (GCD) - Now, doesn’t this sound grand! This is my favourite, it’s a very simple c style api provided by Apple that let’s us ‘dispatch’ blocks of code onto another thread (well technically a queue, but more on that in the next post). In my opinion, and you’re welcome to disagree, this is the best (certainly the easiest) method to use if you just need to introduce a bit of concurrency to make your app a bit more responsive. Most of the time this will be all you need. Stay tuned for a series of tutorials on mastering GCD. Oh, and check out the docs!
- NSOperation - This is basically just a shiny cocoa based object oriented wrapper around GCD. It lacks a certain elegance that GCD has in that you have to subclass NSOperation to use it. Great for encapsulation but a pain if you just need to bounce something off asynchronously. Docs!
Be sure to check back for my upcoming series of posts on mastering GCD.
Oh, and if anything wasn’t clear, or you have any further questions, please do get in touch!