Grand Central Dispatch Tutorial
Jun 25, 2021•3 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
swiftlet serialQueue = DispatchQueue(label: "com.app.serialQueue")
serialQueue.async {
// Tasks execute one at a time
print("Task 1")
}
serialQueue.async {
print("Task 2")
}
Concurrent Queues
swiftlet 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
swiftlet 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
swiftlet 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
swiftlet 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
swiftlet 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
swiftfunc 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
swiftfunc 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
- Thread Safety
swiftclass 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
}
}
- 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.