Apple 的 Dispatch 框架(也称为 Grand Central Dispatch,简称 GCD)是 macOS、iOS、watchOS 和 tvOS 上用于实现 多线程并发编程的核心技术之一。它提供了一个强大而高效的机制,用于在多核设备上调度任务,从而充分利用系统资源。
核心目标 #
GCD 的目标是:
提高应用程序的响应速度和性能
自动管理线程的生命周期,避免手动创建和销毁线程的复杂性
提供线程安全的数据访问和调度控制
基本概念 #
1. 任务(Task) #
GCD 中的任务通常是一个闭包(closure
)或函数,是你希望在后台执行的操作,例如下载、计算或更新 UI。
2. 队列(Dispatch Queue) #
GCD 使用队列调度任务。主要有两种:
串行队列(Serial Queue):每次只执行一个任务,任务按顺序执行。
并发队列(Concurrent Queue):可以同时执行多个任务,但仍按加入顺序开始。
GCD 会负责在线程池中调度这些任务。
常见 API #
创建队列 #
let serialQueue = DispatchQueue(label: "com.example.serial")
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
异步执行任务 #
DispatchQueue.global().async {
// 后台线程执行耗时任务
DispatchQueue.main.async {
// 回到主线程更新 UI
}
}
延时执行 #
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("2 秒后执行")
}
同步执行(⚠️ 警慎使用) #
DispatchQueue.main.sync {
// 同步任务,会阻塞当前线程
}
系统提供的全局队列 #
DispatchQueue.global(qos: .background) // 最低优先级
DispatchQueue.global(qos: .userInitiated) // 用户发起的任务
DispatchQueue.main // 主线程队列,用于 UI 更新
常用 QoS
(Quality of Service):
.userInteractive
:立即响应用户(如滑动、动画).userInitiated
:用户请求并希望尽快完成(如打开文档).utility
:较长时间的后台任务(如下载、处理).background
:不影响用户体验的任务(如预加载)
进阶特性 #
1. DispatchGroup #
用于追踪多个异步任务的完成情况。
let group = DispatchGroup()
queue.async(group: group) {
// 任务 A
}
queue.async(group: group) {
// 任务 B
}
group.notify(queue: .main) {
print("所有任务完成")
}
2. DispatchSemaphore #
用于线程同步控制,控制并发数或资源访问。
3. DispatchWorkItem #
可取消或重复执行的任务单元。
GCD vs OperationQueue #
特性 | GCD(Dispatch) | OperationQueue |
---|---|---|
编程风格 | 函数式,闭包为主 | 面向对象,支持继承和依赖 |
任务依赖 | 不支持 | 支持任务之间的依赖关系 |
任务取消 | 基本不支持 | 支持取消操作 |
灵活度 | 简洁但不易扩展 | 更适合复杂调度需求 |
小结 #
Dispatch 框架(GCD) 是 Apple 提供的轻量级并发工具,适合大多数场景下的并发处理,特别是 UI 和后台任务的调度。它的优势在于:
简洁的 API
高性能调度
自动线程管理
在需要更复杂的任务控制时,可以考虑使用 OperationQueue
或 Swift 的 async/await
特性(iOS 13+/15+)。
示例 #
以下是一个 使用 GCD 实现图片下载并更新 UI 的完整 SwiftUI 示例,适合入门理解 GCD 的实战应用:
示例:使用 GCD 异步下载图片并在主线程更新 UI #
import SwiftUI
struct ContentView: View {
@State private var image: UIImage? = nil
@State private var isLoading = false
let imageURL = URL(string: "https://picsum.photos/300")!
var body: some View {
VStack(spacing: 20) {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(height: 300)
} else {
Rectangle()
.fill(Color.gray.opacity(0.3))
.frame(height: 300)
.overlay(Text("No Image"))
}
if isLoading {
ProgressView()
}
Button("下载图片") {
downloadImage()
}
}
.padding()
}
func downloadImage() {
isLoading = true
// 在后台线程下载图片
DispatchQueue.global(qos: .userInitiated).async {
if let data = try? Data(contentsOf: imageURL),
let downloadedImage = UIImage(data: data) {
// 下载成功后,在主线程更新 UI
DispatchQueue.main.async {
self.image = downloadedImage
self.isLoading = false
}
} else {
// 下载失败,也需要在主线程更新状态
DispatchQueue.main.async {
self.isLoading = false
}
}
}
}
}
要点解释 #
DispatchQueue.global(qos: .userInitiated).async {}
→ 在后台线程中执行耗时的下载任务。DispatchQueue.main.async {}
→ 下载完成后回到主线程更新@State
,从而刷新 UI。@State
→ SwiftUI 的状态变量,驱动界面变化。
这个例子清楚地展示了 GCD 在实际应用中的核心流程:将耗时任务放到后台线程执行,完成后在主线程安全更新界面。这种模式是处理网络请求、文件 IO、数据库操作等常见异步任务的基本范式。