What are mobile engineers’ top painpoints and priorities around building apps in 2024?

Get the report
Crash Reporting

Writing thread-safe code to prevent iOS crashes

Learn about the powerful concepts of serial dispatch queues, atomic properties, and semaphores, enabling you to confidently produce stable iOS apps.

As a mobile app developer, you know that crashes are a worst case scenario. They have the power to leave your users frustrated, tarnish your app reputation, and eat up countless hours of valuable development time. This is why, in the intricate world of iOS development, the importance of thread safety cannot be overstated.

In this blog, we’ll cover three key techniques that you can use to prevent thread-related crashes.

Let’s start by exploring the background, advantages, and disadvantages of writing thread-safe code.

What is thread-safe code?

In iOS, code that is thread-safe can be safely executed by multiple threads concurrently without causing crashes. Thread-safe code is important because it ensures that shared resources are accessed and modified in a controlled manner.

A key component to thread-safe code is employing mutual exclusion methods (e.g., semaphores, serial dispatch queues, atomic operations) when working with concurrent programming. When you ensure that only one resource can be accessed by a thread at a given time, you can prevent race conditions which lead to crashes.

Advantages of thread-safe code

One major advantage of writing thread-safe code is the enhanced reliability and performance of your code. This is achieved by employing the synchronization mechanisms mentioned above to reduce the occurrence of crashes. This makes your app stable, leaves your users satisfied, and frees up more time for innovation in your development.

Another benefit is the reusability of your code. This is possible because thread-safe components can be used in different contexts without worrying about synchronization, thereby saving you time and effort.

Disadvantages of thread-safe code

The downside to thread-safe code is it can have a performance impact. The entire point is to slow down or pause code execution in order to maintain safety.

It’s just like cars traveling through a stop light. The middle of the intersection is a shared resource, and it’s important to prevent crossing traffic from occuring at the same time. To do that, one direction of traffic is given a red light and ordered to wait, while the other direction is given the green light and allowed to process through.

The cars waiting at the red light may get a little impatient, but everybody is happy that this intersection is operating safely without a crash.

Three ways to prevent thread-related crashes

Many of the toughest crashes are thread-related. This is because, when a crash occurs, it can be challenging to determine which thread caused the problem — especially when the crash is intermittent or only occurs under specific circumstances.

To avoid this issue altogether, you can try the following techniques for writing thread-safe code.

Try serial dispatch queues in iOS

Dispatch queues can be thought of as “job” queues. In essence, you request that some work is done on that queue and the runtime decides when and how that job is executed. Serial queues essentially dictate to the runtime, “when retrieving a job from this queue, wait for any previous jobs to finish first.” This means only one thing on this queue can be executed at a time.

You can use this code snippet to achieve this:

// Create a serial dispatch queue
let serialQueue = DispatchQueue(label: "com.example.myqueue")

// Critical section of code
func myCriticalFunction() {
serialQueue.async {
// Access the shared resource
// ...
// Code that may cause a crash
}
}

Use atomic properties in iOS

Atomic properties protect access to the direct object held by that property. They use semaphores under the hood to ensure that only one thing at a time may access the object held at that property.

It is important to note that atomic properties do not protect the contents of the object, only the object itself. So if you have a dictionary as an atomic property, then the dictionary that is assigned to that property will be protected, but what is in the dictionary will not be protected.

Note: The following code snippet is in Objective-C since there is not a standard way in Swift to declare atomic properties. However, you can check out this post for tips on implementing them yourself.

@interface MyClass : NSObject

// Declare an atomic property
@property (atomic) NSNumber *myAtomicProperty;

- (void)myCriticalFunction;

@end

@implementation MyClass

- (void)myCriticalFunction {
  // this "write" access is forced to be thread safe
  self.myAtomicProperty = @(1)
}
@end

Use semaphores in iOS

Semaphores are used to prevent a section of code from executing concurrently. In other words, semaphores can be used to force a section of code to only be able to execute serially.

Here is an example code snippet you can use:

// Declare a semaphore
let semaphore = DispatchSemaphore(value: 1)

// Critical section of code
func myCriticalFunction() {
// Wait for access to the critical section
semaphore.wait()

// Access the shared resource
// ...
// Code that may cause a crash

// Signal that the critical section is available
semaphore.signal()
}

Using these methods above to write thread-safe code is a great approach to improving your mobile app stability.

Final thoughts

As mobile applications become more complex and multi-threaded, mastering thread-safe programming is critical for delivering reliable and high-performing apps.

With the techniques covered in this blog post alongside the right data and insights, you’ll never need to solve the same crash twice. With Embrace intelligent Crash Reporting, mobile development teams can uncover high priority crashes and get to the root cause faster with highly accurate crash grouping, built-in intelligent scoring and deep user context.

Want to get to the bottom of those hard-to-solve iOS crashes quickly? Explore Embrace today.

Embrace Deliver incredible mobile experiences with Embrace.

Get started today with 1 million free user sessions.

Get started free

Build better mobile apps with Embrace

Find out how Embrace helps engineers identify, prioritize, and resolve app issues with ease.