介绍:为什么在 Swift 中需要任务队列?
在现代 iOS 和 macOS 开发中,应用程序的性能直接取决于您管理线程的效率。长时间运行的操作——从网络加载数据、处理图像、与数据库交互——不应阻塞负责界面的主线程(Main Thread)。如果主线程被占用,用户会看到“卡住”的屏幕,系统可能会强制终止应用程序。
任务队列(Task Queues)解决了这个问题。它们允许异步、并行或顺序执行代码,而不会阻塞 UI。在 Swift 中,有三个主要工具可以实现这一点:Grand Central Dispatch (GCD)、OperationQueue 和现代的 async/await 模型(Swift Concurrency)。在本文中,我们将通过实际示例分析它们,并弄清楚何时使用哪种工具。
1. Grand Central Dispatch (GCD):经典方法
GCD 是一个基于 C 的低级库,用于管理线程池。在 Swift 中,可以通过全局队列(DispatchQueue)方便地使用它。
1.1. GCD 中的主要队列类型
- Main Queue — 主线程的串行队列。用于更新 UI。
- Global Queues — 具有不同优先级(QoS:
.userInteractive、.userInitiated、.utility、.background)的并行队列。 - Custom Queues — 您自己创建的队列(可以是串行或并行的)。
1.2. 示例:在后台加载数据并更新 UI
假设我们需要从远程服务器加载 JSON 并在表格中显示结果。
DispatchQueue.global(qos: .userInitiated).async { // 1. 后台工作:加载数据 guard let url = URL(string: "https://api.example.com/data") else { return } let data = try? Data(contentsOf: url) // 2. 返回主线程更新 UI DispatchQueue.main.async { // 更新数据模型或表格 self.tableView.reloadData() }}1.3. 使用 DispatchGroup 进行同步
当需要执行多个并行任务并等待它们完成时,请使用 DispatchGroup。
let group = DispatchGroup()var images: [UIImage] = []
for url in imageURLs { group.enter() DispatchQueue.global().async { if let data = try? Data(contentsOf: url), let image = UIImage(data: data) { images.append(image) } group.leave() }}
group.notify(queue: .main) { print("所有图像已加载:\\(images.count)") // 更新 UI}2. OperationQueue:更高层次的抽象
OperationQueue 构建在 GCD 之上,但提供了额外的功能:取消任务、操作之间的依赖关系、控制最大并发操作数。
2.1. 创建和启动操作
可以通过 BlockOperation 或继承 Operation 来创建操作。
let queue = OperationQueue()queue.maxConcurrentOperationCount = 2 // 限制为 2 个并行任务
let operation1 = BlockOperation { print("操作 1 开始") sleep(2) print("操作 1 完成")}
let operation2 = BlockOperation { print("操作 2 开始") sleep(1) print("操作 2 完成")}
// 设置依赖关系:operation2 仅在 operation1 之后开始operation2.addDependency(operation1)
queue.addOperations([operation1, operation2], waitUntilFinished: false)2.2. 取消操作
与 GCD 不同,操作可以被取消。如果用户离开了屏幕,这一点至关重要,而