iOS Interview Prep 7 — Concurrency Part 1
The purpose of this interview preparation series is to assist you in quickly refining your interview skills and thoroughly preparing for the typical questions asked during an iOS interview. If you find it useful, please leave a comment or tap the like button.
Interview Questions
- What’s the difference between asynchronous and synchronous task
- Explain how Grand Central Dispatch (GCD) works?
- What mechanisms does iOS provide to support multi-threading?
- What are common concurrency issues and how to avoid them?
- Difference between the atomic and nonatomic attributes in Obj-C?
Overview
Concurrency refers to the ability to perform multiple tasks simultaneously. It’s useful for improving the performance and responsiveness of the app, as it allows different parts of the app to run independently without blocking each other. It is one of the top topics asked during an interview, expect questions about GCD and Operation Queues for senior positions.
Quick Recap: Process vs Thread
A process is an application in execution. Each process has its own memory space, system resources, execution state. Processes are isolated from each other and do not share memory or resources.
Threads share the same memory space and resources. Multiple threads can execute at concurrently. Threads can communicate with each other using shared memory.
Quick Recap: Sharing of Resources
The root of many concurrency related evils is the access of shared resources from multiple threads. Anything you share BEtween multiple threads is a potential point of conflict. Whenever you write to a shared resource, the process is not just One machine instruction for the CPU. For Example when you write to a variable, first you ned to read it from memory, then modify the value and finnaly write it back to memory.
Quick Recap: Race condition and thread safety
Race condition is a situation where the outcome of a program depends On the relative timing or oRder of execution of concurrent threads. It can lead to unpredictable and potentially incorrect results. It happens when multiple threads are trying to access the same shared resource. We can use synchronization techniques such as locks to prevent this. The goal is to ensure that only one thread can access the shared resource at a time.
Thread-safe code can safely be called from multiple threads or concurrent tasks without causing any problems such as data corruption or app crashes. Code that isn’t thread-safe can only run in one context at a time.
Quick Recap: Mutual Exclusion
Mutual exclusive access means that only one thread at a time gets access to a certain resource. In IOS, there are many techniques to achieve mutual exclusion. Here are the commonly used approaches:
Locks
In Objective-C, you can use the @synchronized directive to create a lock around a critical section of code. It ensures that only one thread can execute that section at a time. Lock is a non-trivial undertaking and requires use of special instructions on modern CPUs.
It takes an object as an argument, the object us used as a lock to ensure that only one thread can execute the code block at a time. When a thread enters the block, it acquires the lock on the specified Object. If another thread attempts to enter, it will block until the lock is release. It’s similar to semaphore and mutex but the syntax is simpler and concise.
@synchronized(self) {
// Critical section code
}
// Example
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
id<SDWebImageOperation> operation;
@synchronized (self) {
operation = [operationDictionary objectForKey:key];
}
if (operation) {
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
}
@synchronized (self) {
[operationDictionary removeObjectForKey:key];
}
}
}
}Dispatch Queues (GCD) Probably the most common way in practice. GCD provides a mechanism to execute tasks concurrently and serially using dispatch queues. You can use a serial queue to create a critical section and ensure mutual exclusion.
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.sync {
// Critical section code
}Operation Queues They are built on top of Grand Central Dispatch (GCD) and provide additional features such as dependencies. Operations provide much more functionality when you need to keep track of a job or maintain the ability to cancel it. It provides higher-level abstractions than GCD, in object-oriented fashion and automatic support for managing dependencies between operations. Operation queues provide automatic support for tracking the status of operations as they execute — executing, finished, or cancelled
With operation queues, you can specify that one operation should only be executed after another operation has finished, and the operation queue will automatically handle the details of ensuring that the operations are executed in the correct order.
class MyOperation: Operation {
override func main() {
// Critical section code
}
}
let operation = MyOperation()
operationQueue.addOperation(operation)Quick Recap: Atomic vs Nonatomic in Obj-C
Declaring a property as atomic results in implicit locking/unlocking around each access of this property. And locking comes at a cost. And atomic doesn’t ensure thread-safety. To ensure thread safety, additioanl measures such as locks, semaphores, or memory barriers may be necessary.
// Why atomic doesn't ensure thread-safety
Thread A reads the value of an atomic variable,
performs some computation, and then writes the result back to the same variable.
Meanwhile, thread B also reads the value of the same variable,
performs some computation, and writes the result back to the same variable.
Although each of these operations is atomic,
there is no guarantee that the final value of the variable will be correct.
This is because the atomicity of the individual operations
does not prevent them from interfering with each other
Thread A and thread B could access and modify the variable at the same time,
leading to data corruption or other unexpected behavior.Main Queue vs. Main Thread
While there is only one main thread, it is possible for many different queues to execute on this main thread. That means you can call API from non-main queue that is executing on the main thread.
if NSThread.isMainThread() {
image = UIImage(contentsOfFile: filename)
} else {
dispatch_sync(dispatch_get_main_queue()) {
image = UIImage(contentsOfFile: filename)
}
}
// A better solution
if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) ==
dispatch_queue_get_label(dispatch_get_main_queue())) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}iOS Interview Prep Series
iOS Interview Prep 1 — Memory management iOS Interview Prep 2 — Autorelease Pool iOS Interview Prep 3 — Blocks and Closures iOS Interview Prep 4 — Event Handling & Responder Chain iOS Interview Prep 5 — Singletons iOS Interview Prep 6 — Dependency Injection iOS Interview Prep 7 — Concurrency Part 1 iOS Interview Prep 7 — Concurrency Part 2 iOS Interview Prep 8 — View and Layout






