Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial
Have you ever written an app where you tried to do something, and there was a long pause while the UI was unresponsive?
This is usually a sign that your app needs multithreading!
In this tutorial, you’ll get hands on experience with the core multithreading API available on iOS: Grand Central Dispatch.
You’ll take an app that doesn’t use multithreading at all (and hence is very unresponsive), and convert it to use multithreading. You’ll be shocked by the difference!
This tutorial assumes you are familiar with the basics of iOS development. If you are completely new to iOS development, you should check out some of the other tutorials on this site first.
Without further ado, take a swig of soda or chew some bubble gum and begin this tutorial at the same time – and you’re already on your way to multithreading! :]
Why Should I Care?
“Ahem, so why are you telling me this? Why should I care? I don’t care. What’d you have for lunch today?”
If you’re like a certain puppet, you might still be skeptical why you should care about all this multithreading business.
So let’s show you why with a practical example of an app that doesn’t use multithreading at all.
The app is called ImageGrabber, and its job is to go through the HTML of this web page and retrieve all of the images linked within, and display them in a table view so you can look at them more closely.
The cool part is it even downloads zip files and looks for images inside the zip files, such as the free game art zip linked on the site!
Go ahead and tap the “Grab!” button to see if it works.
Wow! It finally worked, but that took forever! The app was parsing the HTML, downloading the images and the zip file, and unzipping the zip file, all on the main thread.
The end result was the user had to sit there for a significant amount of time waiting, not sure if the app was working at all!
The consequences of this are dire: the user might quit the app, the OS might terminate the app for taking too long, or you might get an angry Tomato attacking your treehouse.
Luckily, multithreading comes to the rescue! Instead of putting all of this heavy-duty work on the main thread, we’ll move it to the background with some simple APIs provided by Apple.
Multithreading… and cats!
If you’re already familiar with the concept of multithreading, feel free to skip to the next section. But if you’re completely new – read ahead!
When you think of a program running, you can think of it like a cat with big arrow pointing to the line it’s currently on. The cat moves the arrow as the program advances through its logic, one step at a time.
The problem with the Image Grabber app is we’re basically exhausting our poor cat by doing all the work in the main thread. So before the app could redraw the UI or respond to user events, it has to finish all of that time intensive work of downloading files, parsing HTML, etc.
So how do we give our overworked cat a break? The solution is simple – buy more cats! (In fact I have a friend who is really good at this!)
That way your main cat can be responsible for updating the UI and responding to user events, while your other cats are going around the background downloading files, parsing HTML, and jumping on the tables (get off!)
This is the gist of multithreaded programming. Just like these cats running around performing tasks, a process is broken down into multiple threads of execution.
On iOS, the methods you’re used to implementing (like viewDidLoad, button tap callbacks, etc.) all run on the main thread. You don’t want to perform time intensive work on the main thread, or else you’ll get an unresponsive UI and an overworked cat!
Kids, Do Not Do This At Home
Let’s take a look at the current code and discuss how it works – and why it’s bad!
The root view controller in the app is WebViewController. When you tap the button (grabTapped) it gets the HTML of the current page, and passes it to the ImageListViewController.
In the ImageListViewController’s viewDidLoad, it creates a new ImageManager and calls process. This class, along with ImageInfo, contain all of the time-intensive code, such as parsing the HTML, pulling down the images off the network, and unzipping files.
Let’s see how these two files work:
- ImageManager:processHTML: Uses regular expression matching to search for links in the HTML. This could potentially be time intensive, depending on how large the HTML is. For every zip file it finds, it calls retrieveZip. For every image it finds, it creates a new ImageInfo object, with the initWithSourceURL initializer.
- ImageInfo:initWithSourceURL: Calls getImage to retrieve the image over the network with the synchronous [NSData dataWithContentsOfURL:…] method. Much like the [NSString stringWithContentsOfURL:…] method, this method blocks the flow of execution until it’s complete, which could be a very long time! You almost never want to use this method in your apps.
- ImageInfo:retrieveZip: Similar to the above, uses the dreaded [NSData dataWithContentsOfURL:…] which halts the thread until it completes (do not use!) When it’s done, it calls processZip.
- ImageInfo:processZip: Uses the ZipArchive library to save the downloaded data to disk, unzip it, and look for images inside. Writing to disk and unzipping like this can be a very slow operation, so it’s another instance of work that really shouldn’t be on the main thread.
You might also notice some calls to a delegate method of ImageManager – imageInfosAvailable. This is how the ImageManager notifies the table view when there are new entries to be displayed in the table.
Take a look through and make sure you understand the current flow of execution – and why it’s so bad. You might also find it useful to run it and look at the console log as it runs, and you’ll see some NSLog statements showing where the code is as it runs.
Once you have a good idea of how it currently works, let’s move on and improve it with some multithreading!