引言:为什么你的iPhone在滚动信息流时不会卡顿?
\n\n你是否曾想过,为什么现代iOS应用在同时加载数十张图片、处理点击事件和写入数据库时依然流畅?答案很简单——并发。在Swift(以及其他语言)的世界中,这是最热门的话题之一。如果你正在用Swift编程,却仍然对线程、协程和时髦的async/await感到困惑——欢迎加入我们的行列。今天,我们将把所有内容梳理清楚,让你的代码飞起来,而不是爬行。
本文不仅会列出枯燥的定义。我们将通过实际示例、比较不同方法,并回答一个关键问题:对于你的项目,应该选择老牌的GCD还是全新的Swift Concurrency?
\n\n\n\n线程:一切的基础
\n\n我们从基础开始。线程(Thread)是操作系统可以执行的最小处理单元。想象一条流水线:一个线程就是一条任务流动的线路。如果你的处理器是单核,线程会依次执行,但通过快速切换制造出并行的假象。在多核处理器上,线程可以真正并行执行。
\n\n在Swift中,长期以来处理线程的主要工具是Grand Central Dispatch (GCD)。你只需创建一个队列,然后将闭包提交进去:
\n\nDispatchQueue.global(qos: .background).async {\n // 后台执行繁重任务\n let result = heavyComputation()\n DispatchQueue.main.async {\n // 在主线程更新UI\n self.label.text = result\n }\n}\n\n很简单,对吧?但GCD有一些注意事项:
\n- \n
- 你需要自己负责回到主线程(很容易忘记并导致崩溃)。 \n
- 没有内置的任务取消支持(需要手动编写标志位)。 \n
- 处理依赖操作链时容易陷入闭包嵌套(即所谓的“回调地狱”)。 \n
尽管如此,GCD在遗留项目中仍然活跃使用。但现代Swift提供了更优雅的解决方案。
\n\nSwift中的协程?不,这不是Go
\n\n当人们提到协程时,通常会想到Go语言。Swift没有直接的协程对应物,但有轻量级任务(tasks),其行为非常相似。它们不绑定到特定线程——系统自行决定在哪个线程上执行任务,并且可以在线程之间切换,而无需为每次调用创建新线程。
\n\n与每个线程占用约8KB栈空间不同,Swift Concurrency的任务几乎不占用资源。你可以启动数千个任务而无需担心内存溢出。这就是所谓的“无痛并发”。
\n\n看看它的样子:
\n\nTask {\n let image = await downloadImage(from: url)\n await MainActor.run {\n self.imageView.image = image\n }\n}\n\n注意到了吗?没有DispatchQueue.main.async。只有await MainActor.run。干净、清晰、安全。
async/await:一场革命
\n\n随着Swift 5.5的发布,内置的async/await支持出现了。这不仅仅是语法糖——而是一次范式转变。现在,你可以像编写同步代码一样编写异步代码,同时不会阻塞线程。
\n\n声明一个异步函数很简单:
\n\nfunc fetchUserData() async throws -> User {\n let url = URL(string: "https://api.example.com/user")!\n let (data, _) = try await URLSession.shared.data(from: url)\n return try JSONDecoder().decode(User.self, from: data)\n}\n\n调用它更简单:
\n\nTask {\n do {\n let user = try await fetchUserData()\n print("User nam