avatarXiao.J

Summary

The provided content is an iOS interview preparation guide focused on concurrency, detailing the differences between synchronous and asynchronous tasks, the workings of Grand Central Dispatch (GCD), iOS multi-threading support mechanisms, common concurrency issues, and the distinction between atomic and nonatomic attributes in Objective-C.

Abstract

The article "iOS Interview Prep 7 — Concurrency Part 1" serves as a comprehensive guide for developers preparing for iOS interviews, with an emphasis on understanding concurrency. It outlines key concepts such as the differences between asynchronous and synchronous tasks, explaining how Grand Central Dispatch (GCD) facilitates concurrent and serial task execution. The text delves into various mechanisms iOS provides for multi-threading, including GCD and Operation Queues, and discusses common concurrency issues like race conditions and the importance of thread safety. It also clarifies the use of locks for mutual exclusion and the distinction between atomic and nonatomic properties in Objective-C, highlighting that atomic properties do not guarantee thread safety. The article aims to equip readers with the knowledge to handle concurrency challenges effectively and to write thread-safe code.

Opinions

  • The article implies that a solid understanding of concurrency is crucial for iOS developers, especially for those interviewing for senior positions.
  • It suggests that while GCD is a powerful tool for concurrency, developers should also be familiar with Operation Queues for more complex task management and dependencies.
  • The text emphasizes the importance of mutual exclusion when accessing shared resources to prevent race conditions, indicating that this is a common pitfall in concurrent programming.
  • It is noted that atomic properties in Objective-C, while providing a level of thread safety, are not a panacea for concurrency issues and that additional synchronization mechanisms may be necessary.
  • The article conveys that understanding the difference between the main queue and the main thread is important for proper UI updates and avoiding common mistakes when dispatching tasks.

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

iOS
iOS App Development
iOS Development
Swift
Interview
Recommended from ReadMedium