Grand Central Dispatch Tutorial

Jun 25, 20213 min read

Grand Central Dispatch (GCD) is a powerful framework in iOS that helps developers manage concurrent operations and improve app performance. Let's explore how to use it effectively.

Understanding GCD Basics

GCD provides a simple and efficient way to execute code concurrently using dispatch queues.

Main Queue vs Background Queues

swift
// Main Queue - UI Updates
DispatchQueue.main.async {
    self.titleLabel.text = "Updated"
    self.imageView.image = downloadedImage
}

// Background Queue - Heavy Operations
DispatchQueue.global(qos: .background).async {
    // Perform time-consuming task
    let result = processLargeData()
    
    // Update UI on main queue
    DispatchQueue.main.async {
        self.updateUI(with: result)
    }
}

Quality of Service (QoS)

GCD provides different QoS levels to prioritize tasks:

swift
// User Interactive - Highest Priority
DispatchQueue.global(qos: .userInteractive).async {
    // Animations, event handling
}

// User Initiated
DispatchQueue.global(qos: .userInitiated).async {
    // Tasks requiring immediate results
}

// Utility
DispatchQueue.global(qos: .utility).async {
    // Long-running tasks with progress indicators
}

// Background - Lowest Priority
DispatchQueue.global(qos: .background).async {
    // Prefetching, maintenance tasks
}

Custom Serial Queues

swift
let serialQueue = DispatchQueue(label: "com.app.serialQueue")

serialQueue.async {
    // Tasks execute one at a time
    print("Task 1")
}

serialQueue.async {
    print("Task 2")
}

Concurrent Queues

swift
let concurrentQueue = DispatchQueue(
    label: "com.app.concurrentQueue",
    attributes: .concurrent
)

concurrentQueue.async {
    // These tasks can run simultaneously
    print("Task 1")
}

concurrentQueue.async {
    print("Task 2")
}

DispatchGroup for Task Coordination

swift
let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)

// Task 1
group.enter()
queue.async {
    // Perform task
    print("Task 1 completed")
    group.leave()
}

// Task 2
group.enter()
queue.async {
    // Perform task
    print("Task 2 completed")
    group.leave()
}

// Notification when all tasks complete
group.notify(queue: .main) {
    print("All tasks completed")
}

Dispatch Work Items

swift
let workItem = DispatchWorkItem {
    // Perform task
    print("Work item executed")
}

// Execute after delay
DispatchQueue.main.asyncAfter(
    deadline: .now() + 2,
    execute: workItem
)

// Cancel if needed
workItem.cancel()

Dispatch Semaphore for Resource Management

swift
let semaphore = DispatchSemaphore(value: 2)
let queue = DispatchQueue.global(qos: .userInitiated)

for i in 1...5 {
    queue.async {
        semaphore.wait()  // Wait for resource
        
        // Perform task
        print("Task \(i) started")
        Thread.sleep(forTimeInterval: 2)
        print("Task \(i) completed")
        
        semaphore.signal()  // Release resource
    }
}

Dispatch Barriers

swift
let concurrentQueue = DispatchQueue(
    label: "com.app.queue",
    attributes: .concurrent
)

// Regular concurrent tasks
concurrentQueue.async {
    print("Read 1")
}
concurrentQueue.async {
    print("Read 2")
}

// Barrier for write operation
concurrentQueue.async(flags: .barrier) {
    print("Writing data")
}

// More concurrent tasks
concurrentQueue.async {
    print("Read 3")
}

Common Use Cases

Image Processing

swift
func processImage(_ image: UIImage, completion: @escaping (UIImage?) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        // Process image
        let processedImage = image.applyFilter()
        
        DispatchQueue.main.async {
            completion(processedImage)
        }
    }
}

Network Operations

swift
func fetchData(completion: @escaping (Data?) -> Void) {
    let queue = DispatchQueue.global(qos: .utility)
    
    queue.async {
        // Network request
        let data = // ... fetch data ...
        
        DispatchQueue.main.async {
            completion(data)
        }
    }
}

Best Practices

  1. Thread Safety
swift
class ThreadSafeArray<T> {
    private var array = [T]()
    private let queue = DispatchQueue(
        label: "com.app.threadSafeArray",
        attributes: .concurrent
    )
    
    func append(_ element: T) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }
    
    func element(at index: Int) -> T? {
        var result: T?
        queue.sync {
            guard index < array.count else { return }
            result = array[index]
        }
        return result
    }
}
  1. Avoiding Deadlocks
swift
// Wrong - May cause deadlock
DispatchQueue.main.sync {
    // Some work
}

// Correct - Use async when possible
DispatchQueue.main.async {
    // Some work
}

Conclusion

GCD is essential for:

  • Improving app responsiveness
  • Managing concurrent operations
  • Efficient resource utilization
  • Background processing

Remember to:

  • Use appropriate QoS levels
  • Avoid blocking the main thread
  • Handle task coordination properly
  • Implement proper error handling

For more information, visit the Apple Developer Documentation.