ProgrammingiOS Developer

How to implement thread-safe interaction with mutable data in Swift? Describe the approaches and issues that may arise from improper implementation.

Pass interviews with Hintsage AI assistant

Answer

In Swift, to ensure thread safety, GCD (Grand Central Dispatch) and queues (DispatchQueue), NSLock, as well as modern mechanisms like actor (starting from Swift 5.5) are often used.

The main idea – access to mutable data occurs either strictly through serial queues or using synchronizers (locks).

Example with GCD:

class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }

Modern Approach: Actors (Swift 5.5+):

actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }

Both approaches help avoid data races and maintain state consistency.

Trick Question

Question:

If you use DispatchQueue.sync on the Main Queue, what will happen and why?

Answer: A deadlock will occur because the Main Queue is already executing a task and is waiting for the synchronous operation on the same queue to complete. Consequently, the queue will not be able to process the next task until the current one finishes, which will never happen.

Example:

DispatchQueue.main.sync { // Deadlock: this line will never be executed }

Examples of Real Errors Due to Lack of Knowledge on the Topic


Story

In the project, a mutable collection was used that was accessed simultaneously from multiple threads without synchronization. This led to application crashes and elusive bugs due to data races: elements were occasionally lost, and sometimes array bounds were exceeded.


Story

A developer incorrectly used DispatchQueue.sync on the main queue at runtime, which caused a deadlock and a complete stop of the UI for users after an update was released. An urgent fix and rollback of the release were required.


Story

When trying to make a class thread-safe with NSLock, the lock/unlock implementation for all exit paths from the method (for example, in the case of an error or return inside a guard) was forgotten, leading to potential deadlocks and serious performance issues for the application.