iOS Multithreading Strategies For Beginners

Wenzhi Lin
5 min readApr 1, 2017

Concurrency is one of the most complicated (pronounced scary) topics in programing. It is a very powerful tool which can bring magic to your app’s performance. But, when used wrongly, it will also introduce nasty issues. As a beginner programmer in iOS, you cannot improve without knowing how to swing this double-edged sword. Today, I will help you with multithreading in iOS.

What are threads?

Apple’s documentation defines threads as “a relatively lightweight way to implement multiple paths of execution inside of an application.” To make it simpler, you can imagine each thread is a cookie monster, and each cookie monster has their own queue of cookies(code waiting for execution).

Multithreading is great.

iOS requires that UI-related code be executed in the main thread. Imagine the UI code as M&M cookies; thus, all the M&M cookies can only go to the main cookie monster. If we only have one cookie monster with many different kinds of cookies (M&M for UI, chocolate chip for network connections, etc), the M&M cookies will execute very slowly as there are too many other cookies. We will see the app is not very responsive, or it will even freeze, because the main monster cannot get to the M&M cookies fast enough.

Adopting concurrency is like having multiple cookie monsters eating the cookies. If you can assign one monster to eat only the chocolate chip (network handing) cookies, one monster to eat the peanut butter (data storing and caching) cookies, and only feed the M&M (UI) cookies to the main cookie monster, then all the cookies will get eaten in a much faster and cleaner way. That is how multiple threads boost the performance of your app. There are two common ways you can put more cookie monsters in your app — Grand Central Dispatch and NSOperationQueue.

1st Way: Grand Central Dispatch (GCD)

As this is a guideline for beginners, I’ll start introducing how to use GCD in the most simple way.

Let’s name a cookie monster’s cookie line to be a “dispatch queue”. You can get the common dispatch queues by the following methods:

  • dispatch_get_main_queue: This is the queue for the main thread.
  • dispatch_get_global_queue: The system provides each application with four concurrent dispatch queues, and you can use this method to get any shared concurrent queues. They are good for background tasks.
  • dispatch_get_current_queue: This returns the current queue

You can also create you your own serial dispatch queue by dispatch_queue-create(queue_name, NULL).

Now you are standing at the conveyor belt (queue) and starting to pack each cookie (code) into a bag (block). You can distribute the cookies in two ways: (1) you put the packaged cookie on a queue, and continue to work on making other cookies and doing other tasks. In this case, you don’t care when the cookie will reach the cookie monster, or how many cookies are before or after that cookie; (2) This is an important cookie that you stop to wait for the cookie to reach the cookie monster. In this case, you are trying to make sure the cookie is eaten before doing anything else.

For the first case, you are dispatching a code block to the queue asynchronously. It looks like this in code:

For the second case, you are dispatching a code block to the queue synchronously. This will block the current thread until the task is finished:

2nd Way: NSOperationQueue

GCD is quite a good and simple way to distribute cookies concurrently. However, when your cookie factory is getting sophisticated, you will want more control over the cookies and the cookie lines. It’s time to look into NSOperation and NSOperationQueue.

NSOperation is a smart box that you can put your cookie in. You can choose different kinds of boxes; some of them can contain multiple cookies, and some can handle custom sized cookies. You can instruct the cookie monsters how to eat the cookie in each box, as well as mark them by different priority. The boxes have buttons allowing you to mark a cookie as “expired” (canceling an operation), and they have labels to show you if a cookie is being eaten or already finished.

NSOperationQueue is an upgraded conveyor belt to handle the smart boxes. You can add as many belts as you want, and you can put several smart cookie boxes at the same time in one belt. These upgraded conveyor belts have “handles” so you can suspend or resume a NSOperationQueue. You can also cancel all the boxes on a belt, or wait for a queue to finish delivering all the boxes to the cookie monster. Quite a powerful tool for concurrency, isn’t it?

When cookie monster becomes real monster

If you didn’t assign the cookies to cookie monsters in a careful way, bad things will happen. For example, two cookie monsters might try to eat the same cookie at the same time and cause a fight. To be more specific, when two threads try to change a property to different values at the same time, or when a thread is changing a property while another thread is trying to read that property, an unwanted result may appear, or even crash the app. This kind of bug is one of the hardest to debug, because it is difficult to reproduce the exact issue while keeping track of all the threads.

A fight between two cookie monsters can be pretty bloody, and a resource fight between two threads can lead you to unexpected disasters. Next, I will introduce a fast and easy way to save you from cookie monsters fights.

Fast solution: Synchronization

With synchronization, you can lock the access to a cookie and only allow one cookie monster to open it at a time. You can follow Apple’s guidelines to find multiple ways to create a lock, but the simplest way is to use the @synchronized directive. It only needs an Objective-C object of any type to be the token, like this:

Though synchronization is easy to implement, this solution has two downsides:

  • It can easily lose the concurrent advantage of multiple threading. The lock forces the app to serialize the process of the code with the same lock. If you synchronize everywhere, you will make your code slow again.
  • It can introduce Deadlocks. Imagine that the method addM&MToCookie receives the token to run the code, but it needs to wait for bakeCookieDough to finish first. But bakeCookieDough has the same lock and is waiting for the addM&MToCookie to finish and release the token… No code will get executed and no cookie for cookie monster!

Conclusion

That’s the fast tutorial about multithreading in Objective-C and iOS. Of course, there is much more to learn. I recommend that you start from Apple’s documentation “Concurrent Programing Guide”: https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1.

Wenzhi Lin is a Senior iOS Engineer at Appboy, Inc.

--

--

Wenzhi Lin

A climber who enjoys skiing and scuba diving, and writes iOS code during the day. Made in China, evolving in the USA.