Welcome to Day 23 of the 31 Days of iOS. Yesterday we wrapped up a two part series on using the camera and photo gallery to get access to pictures. Today we’re going to switch gears and talk about using background threads. We’ll first explain why it’s important to know how to do this and then we’ll go through a simple example of how to do it. We’ll be starting with a brand new project, but if you’d like to follow along with the completed code, you can access it here.
Why use background threads
When you build your applications, you need to be extremely cognizant of the processing that goes on on the UI thread. If the operations you’re performing are too resource and time intensive, the user interface will be unresponsive which always leads to a very poor user experience. If you want a very quick example of this in action, create a new project and alter the ViewController.m’s viewDidLoad method to be the following:
Here we’re putting a sleep (using NSThread sleepForTimeInterval) in a for loop. The effect of this is that we’re doing some very time intensive processing. It will take a full six seconds before the viewDidLoad method is finished. This means that our UI won’t load until these 6 seconds of processing are complete. Now the for loop in the code above is very meaningless and would never be found in a good application (or even a decently coded one). However, it wouldn’t be hard to do a similar amount of REAL processing. Just think of needing to pull down several images when loading or having to hit a slow responding server or do a lot of file processing. Now some of these things already have ways of wrapping a lot of the processing so you wouldn’t have to worry about it (such as doing asynchronous web requests using NSURLConnection’s sendAscynchronousRequest. In situations where iOS and objective-c don’t wrap the functionality in a way that prevents it from occurring on the UI thread, you can use background threads to prevent hanging up the UI. Let’s dive in and look at an example of how to do this now.
Creating our project and handling the UI
Open up Xcode and choose File, New, Project. We’ll use a Single View Application and name it DayTwentyThree:
Open up MainStoryboard.storyboard and let’s handle our initial UI. We’re going to drop three round rect buttons and a label on our UI. Make sure you set the Lines property of the label in the Attributes Inspector to 0 so it will allow us to print out multiple lines. Your UI should look like this:
After that, open up the Assistant Editor in the top right of Xcode and control + click and drag from each button and the label over to the view controller code. You’ll want to create actions for each button and an outlet for the label. When you’re done, the ViewController.h file will look like this:
Now we can implement some processing.
Reexamining slow foreground processing
Open up ViewController.m. The first thing we’re going to do is duplicate the code we saw above so we can better understand how it impacts the UI thread. Let’s put that for loop with the sleep statements into the tappedProcessInForeground method:
This code is the same as we saw above. We have a for loop and then calls to the NSThread method sleepForTimeInterval. Before we can runt he app and see how this really affects UI interaction, let’s also implement the tappedUpdateLabel method:
All we’re doing in this method is getting the current time and converting it into an NSString. We then use that to put the current time at the top of the label (by appending the label text to the current time’s NSString value). Now we can run our app and better understand locking or hanging the UI thread.
Locking the UI thread
Run your app in the simulator (or on a device). The first thing you’ll want to do, is tap the second button, the Update Label button. You should see the current time appear at the top of the label below the buttons:
Now, what you should do is tap the Process in Foreground button, and then tap the Update Label button several times immediately after. What should happen is that the Process in Foreground button will stay blue for a few seconds and the label won’t update:
After 6 seconds, the processing will finish and the Process in Foreground button will return to white. Additionally, the taps of the Update Label button will finally go through resulting in the time showing in the label for a time at least a few seconds after it was actually tapped (depending on how fast you tap):
The reason this happens is that as long as we’re doing all that processing within the method directly called by the UI, it’s preventing anything from being processed. So, even though you’ve tapped the button to update the label, it doesn’t update. However, the taps DO go through once the UI thread returns. This is really the best we can hope for because we still want whatever the user has done to go through after our processing returns, but it’s far from optimal. Instead, what would be best, is for the processing to happen in a background thread where it won’t lock the UI thread. We’ll do that next.
Processing on the background thread
We’ll next implement the tappedProcessInBackground method. We’ll first implement this method to just do our processing in the backend and not block the UI thread. The code for this is actually not very complicated:
We first call dispatch_asycn and send in the dispatch_get_global_queue method to get the DISPATCH_QUEUE_PRIORITY_BACKGROUND. This background queue is kind of a default very low priority queue that can be used for low priority processing. dispatch_get_global_queue is used to get shared concurrent queues, of which the background queue is one of them. We also pass a block of code that will do the same processing we did above. Now if you run your app and tap the Process in Background button, you’ll be able to tap the Update Label method immediately after and it will update the label. This is because we’re doing our EXTREME processing in a background thread and not on the UI thread.
Updating the UI when our background thread is done
It’s important to know how to update the UI thread once you’re done with your expensive processing as you’ll often be pulling or opening something that will then affect the UI. We’ll look at how to do that now. The first thing we’re going to do is add another method to the ViewController.m file:
The purpose of this method is to just update the label with the text Background Done!. We’re going to call this method when our background processing is done. Now we’ll go back and change the tappedProcessInBackground method:
The only difference to this method is the call after our for loop. The changes are another call to dispatch_async. In this call we call dispatch_get_main_queue and pass in a block which will call the updateLabelWhenBackgroundDone method. The dispatch_get_main_queue get’s the application’s main queue which is what the UI is running on. So we can use this to then call methods on that main queue. Now, when we run our app, we can tap the Process in Background method, then tap the Update Label button several times, and when the background thread is done processing, our UI will be updated:
Today we talked about why it’s important to consider what sort of processing you’re doing on your UI thread and how you can see the impact of doing too much processing. Today’s example was fairly arbitrary as it wasn’t really doing anything, however, there are many different things you might do in an application that could take a long time. If you find yourself doing these things in your app, using a background thread is a good idea. It’s important to know that we just skimmed the surface of what you can do with, and how you can do, background threads. If you’re interested in doing anything more advanced, I’d recommend you do some research into dispatch queues, Grand Central Dispatch, and NSOperation. The point is that the iOS threading system can get very complex if you want it to. You may need to go beyond the global background queue. You can download the completed code from today’s walkthrough here.