简介
Swift Concurrency(async/await)是从 Swift 5.5 开始引入的一套并发编程模型,用来替代传统的回调(callback)、闭包嵌套(callback hell)、以及部分 GCD 使用场景,让异步代码写起来像同步代码一样清晰。
async / await
- async:标记函数是 异步函数
- await:表示 等待异步结果
本质:await 会“挂起当前任务”,但不会阻塞线程
示例
func fetchUser() async -> String {
return "Tom"
}
func loadData() async {
let user = await fetchUser()
print(user)
}
async throws
支持错误处理(替代 callback 的 error)
enum NetworkError: Error {
case failed
}
func fetchData() async throws -> String {
throw NetworkError.failed
}
func load() async {
do {
let result = try await fetchData()
print(result)
} catch {
print("error: \(error)")
}
}
Task
Task 是并发执行的基本单位,类似 GCD 的 block,也是一个对象。
- 普通 Task
Task {
let data = await fetchUser()
print(data)
}
let task = Task {
...
}
- Detached Task(独立线程)
Task.detached {
await doSomething()
}
区别:
Task:继承当前 Actor / 优先级 / 上下文
detached:完全独立(慎用)
async let
并发执行
场景:多个接口同时请求
func loadData() async {
async let user = fetchUser()
async let posts = fetchPosts()
let result = await (user, posts)
print(result)
}
TaskGroup
任务组
场景:批量请求 / 并发处理列表
func fetchAll() async {
// 每个子任务返回值类型是 String
await withTaskGroup(of: String.self) { group in
// 动态创建多个异步任务 并发执行,逐个获取结果
for i in 1...3 {
group.addTask {
return "Task \(i)"
}
}
for await result in group {
print(result) // 打印每个任务的结果;顺序不确定-先完成先打印
}
}
}
生命周期:当代码执行完withTaskGroup { ... },会自动等待所有子任务完成,然后自动释放资源。另外,也能自动取消未完成任务(如果提前退出)。任一任务抛错 就会全部取消。
手动取消
group.cancelAll(),如 退出页面
使用场景:
- 动态任务数量 (对比 async let - 任务数固定)
- 适合列表/批量处理
1、批量接口请求
func fetchUsers(ids: [Int]) async -> [User] {
var result: [User] = []
await withTaskGroup(of: User.self) { group in
for id in ids {
group.addTask {
return await fetchUser(id: id)
}
}
for await user in group {
result.append(user)
}
}
return result
}
2、批量下载
func downloadImages(urls: [URL]) async -> [UIImage] {
var images: [UIImage] = []
await withTaskGroup(of: UIImage?.self) { group in
for url in urls {
group.addTask {
return try? await downloadImage(url: url)
}
}
for await img in group {
if let img = img {
images.append(img)
}
}
}
return images
}
3、并发计算任务(CPU)
func processData(items: [Int]) async {
await withTaskGroup(of: Void.self) { group in
for item in items {
group.addTask {
heavyWork(item)
}
}
}
}
4、限制最大并发数(手动处理)
func fetchWithLimit(ids: [Int]) async {
let maxConcurrent = 2
await withTaskGroup(of: String.self) { group in
var iterator = ids.makeIterator()
// 先启动前 N 个任务
for _ in 0..<maxConcurrent {
if let id = iterator.next() {
group.addTask {
return await fetchUser(id: id)
}
}
}
// 每完成一个,就补一个
for await result in group {
print(result)
if let nextId = iterator.next() {
group.addTask {
return await fetchUser(id: nextId)
}
}
}
}
}
Actor
线程安全方案
用于解决数据竞争问题(替代锁)
actor Counter {
private var value = 0
func increment() {
value += 1
}
func getValue() -> Int {
return value
}
}
let counter = Counter()
Task {
await counter.increment()
let v = await counter.getValue()
print(v)
}
Actor = 自动串行队列 + 数据隔离
对比
| 方案 | 问题 |
|---|---|
| NSLock | 易死锁 |
| DispatchQueue | 需要手动管理 |
| Actor | 天然安全 |
实战应用场景
- 网络请求
func fetchUser() async -> User { ... }
func fetchPosts() async -> [Post] { ... }
func load() async {
let user = await fetchUser()
let posts = await fetchPosts()
}
——对比旧方案
fetchUser { result in
fetchPosts { posts in
// 嵌套地狱
}
}
- 多个接口并发请求
多个请求任务并行执行,等待异步结果
func loadPage() async {
async let banner = fetchBanner()
async let list = fetchList()
async let profile = fetchProfile()
let (b, l, p) = await (banner, list, profile)
}
- 主线程更新UI
func loadData() {
Task {
let data = await fetchData()
await MainActor.run {
self.label.text = data
}
}
}
或 使用@MainActor
@MainActor
func updateUI() {
label.text = "Hello"
}
- 取消任务
let task = Task {
let data = await fetchData()
}
task.cancel()
- 图片加载
func downloadImage(url: URL) async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
return UIImage(data: data)!
}
let task = Task {
let image = try await downloadImage(url: url)
cell.imageView.image = image
}
注意:需要处理 cell 复用问题(取消任务)-》 避免图片错位
override func prepareForReuse() {
task?.cancel()
}
- 顺序依赖
func process() async {
let token = await login()
let data = await fetchData(token: token)
let result = await parse(data)
}
对比
| 方案 | 特点 |
|---|---|
| GCD | 底层强,但难维护 |
| Operation | 可控但复杂 |
| async/await | 简洁 + 可读性强 |
本质: • GCD:你管理线程 • async/await:系统帮你调度
总结
Swift Concurrency 本质就是:
用“同步写法”写“异步代码”,并且保证线程安全