Middle
How to implement multithreading using queues in iOS?
sobes.tech AI
Answer from AI
Multi-threading in iOS using queues is implemented with Grand Central Dispatch (GCD) and Operation Queues.
GCD is a low-level API based on task management and queues (DispatchQueue).
Main concepts of GCD:
- Dispatch Queues: Manage the execution of tasks (closures or functions). There are serial queues and concurrent queues.
- Serial Queues: Tasks are executed strictly sequentially, one at a time. They guarantee order and prevent race conditions when accessing shared resources.
let serialQueue = DispatchQueue(label: "com.example.mySerialQueue") serialQueue.async { // Task 1 } serialQueue.async { // Task 2 (will start after Task 1 finishes) } - Concurrent Queues: Tasks are executed in parallel as much as possible.
let concurrentQueue = DispatchQueue(label: "com.example.myConcurrentQueue", attributes: .concurrent) concurrentQueue.async { // Task 1 (can run simultaneously with Task 2) } concurrentQueue.async { // Task 2 }
- Serial Queues: Tasks are executed strictly sequentially, one at a time. They guarantee order and prevent race conditions when accessing shared resources.
- Main Queue: A special serial queue associated with the main thread of the application. All UI updates should be performed on this queue.
DispatchQueue.main.async { // UI update } - Global Concurrent Queues: Provided by the system and used for background tasks with different QoS priorities:
.userInteractive,.userInitiated,.default,.utility,.background.DispatchQueue.global(qos: .userInitiated).async { // Execute user-initiated task } - Synchronous vs Asynchronous execution:
async: The task is placed in the queue and executed on a background thread; the current thread is not blocked.sync: The task is placed in the queue, and the current thread is blocked until the task completes. Usingsyncon the same queue as the current thread can lead to deadlock.
Operation Queues are a higher-level API built on top of GCD. They use Operation objects (or subclasses like BlockOperation, ClosureOperation in Swift, NSBlockOperation, NSOperation in Objective-C) to encapsulate tasks.
Main advantages of Operation Queues:
- Dependencies: You can specify that one operation cannot start until another finishes.
let operation1 = BlockOperation { /* Task 1 */ } let operation2 = BlockOperation { /* Task 2 */ } operation2.addDependency(operation1) // task2 will run after task1 let operationQueue = OperationQueue() operationQueue.addOperation(operation1) operationQueue.addOperation(operation2) - Priorities: You can set priorities for each operation.
- Cancellation: Operations can be canceled.
- States: Operations have states (
isReady,isExecuting,isFinished,isCancelled). - KVO Observers: You can observe the state of operations.
- Maximum concurrent operations: You can limit the number of operations running simultaneously by setting
maxConcurrentOperationCount. Setting it to 1 makes the queue serial, similar to a serial GCD queue.let opQueue = OperationQueue() opQueue.maxConcurrentOperationCount = 4 // Limit to 4 concurrent operations
Choosing between GCD and Operation Queues:
- GCD: Preferable for simple background tasks, executing code on the main queue, creating custom serial queues. It is more lightweight.
- Operation Queues: Better suited for complex scenarios: tasks with dependencies, cancelable tasks, control over the number of concurrent tasks, more structured operation management.
Example of synchronizing data access using a serial queue:
class DataManager {
private var data: [String] = []
private let queue = DispatchQueue(label: "com.example.dataQueue")
func addData(_ item: String) {
queue.sync {
// Safe access to data, as it occurs within a serial queue
data.append(item)
}
}
func getData() -> [String] {
var result: [String] = []
queue.sync {
// Safe reading of data
result = data
}
return result
}
}