名称:swift-concurrency 描述:构建、审计和重构使用现代并发模式(Swift 6+)的Swift代码的指南。此技能应用于处理async/await、Tasks、actors、MainActor、Sendable类型、隔离域,或将传统回调/Combine代码迁移到结构化并发。涵盖易用并发设置、隔离参数和常见陷阱。
Swift 并发
概述
此技能提供使用现代并发模式编写线程安全Swift代码的指导。它涵盖三个主要工作流程:构建新的异步代码、审计现有代码以发现问题,以及将传统模式重构为Swift 6+。
核心原则:隔离默认继承。使用易用并发时,代码从MainActor开始,并自动传播到程序中。在需要时显式选择退出。
工作流程决策树
你在做什么?
│
├─► 构建新的异步代码
│ └─► 参见下方“构建工作流程”
│
├─► 审计现有代码
│ └─► 参见下方“审计清单”
│
└─► 重构传统代码
└─► 参见下方“重构工作流程”
构建工作流程
编写新的异步代码时,遵循此决策过程:
步骤1:确定隔离需求
此类型是否管理UI状态或与UI交互?
│
├─► 是 → 标记为@MainActor
│
└─► 否 → 它是否具有跨上下文共享的可变状态?
│
├─► 是 → 考虑:它能始终在MainActor上吗?
│ │
│ ├─► 是 → 使用@MainActor(更简单)
│ │
│ └─► 否 → 使用自定义actor(需要理由)
│
└─► 否 → 保持非隔离(易用并发的默认)
步骤2:设计异步函数
// 推荐:继承调用者的隔离(在任何地方工作)
func fetchData(isolation: isolated (any Actor)? = #isolation) async throws -> Data {
// 在调用者所在的actor上运行
}
// 当需要时:必须在后台运行的CPU密集型工作
@concurrent
func processLargeFile() async -> Result { }
// 避免:没有明确选择的非隔离异步
func ambiguousAsync() async { } // 这在何处运行?
步骤3:处理并行工作
// 对于已知数量的独立操作
async let avatar = fetchImage("avatar.jpg")
async let banner = fetchImage("banner.jpg")
let (a, b) = await (avatar, banner)
// 对于动态数量的操作
try await withThrowingTaskGroup(of: Void.self) { group in
for id in userIDs {
group.addTask { try await fetchUser(id) }
}
try await group.waitForAll()
}
步骤4:SwiftUI集成
struct ProfileView: View {
@State private var avatar: Image?
var body: some View {
avatar
.task { avatar = await downloadAvatar() } // 在消失时自动取消
.task(id: userID) { /* 当userID改变时重新加载 */ }
}
}
// 对于用户操作
Button("保存") {
Task { await saveProfile() } // 继承MainActor隔离
}
审计清单
审查Swift并发代码时,检查这些问题:
关键问题(必须修复)
- [ ] 阻塞合作池:在异步上下文中查找
DispatchSemaphore.wait()、DispatchGroup.wait()或类似阻塞调用 - [ ] 数据竞争:非Sendable类型跨越隔离边界而没有适当处理
- [ ] 非Sendable类型中的非隔离异步:这些仅从非隔离上下文工作
常见问题(应该修复)
- [ ] actor过度使用:没有理由的自定义actor(参见参考文献中的“Actor理由测试”)
- [ ] 不必要的
MainActor.run:通常应该在函数上使用@MainActor代替 - [ ] 认为异步等于后台:异步函数内的同步CPU工作仍然阻塞
- [ ] 在结构化可行时使用非结构化Tasks:
Task { }而不是async let或TaskGroup - [ ] 缺少取消处理:长操作应该检查
Task.isCancelled
SwiftUI特定
- [ ] 视图非MainActor隔离:SwiftUI视图应该是
@MainActor(或使用@Observable) - [ ] 从分离任务访问@State:必须跳回MainActor
Sendable合规性
- [ ] @unchecked Sendable过度使用:应该罕见且有理由
- [ ] 使一切都Sendable:并非所有类型都需要跨越边界
- [ ] 非Sendable闭包逃逸:检查闭包捕获
重构工作流程
从回调到async/await
// 之前:基于回调
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, _, error in
if let error { completion(.failure(error)); return }
// ...
}.resume()
}
// 之后:使用continuation的async/await
func fetchUser(id: Int) async throws -> User {
try await withCheckedThrowingContinuation { continuation in
fetchUser(id: id) { result in
continuation.resume(with: result)
}
}
}
从DispatchQueue到Actors
// 之前:基于队列的保护
class BankAccount {
private let queue = DispatchQueue(label: "account")
private var _balance: Double = 0
var balance: Double {
queue.sync { _balance }
}
func deposit(_ amount: Double) {
queue.async { self._balance += amount }
}
}
// 之后:Actor(如果真正需要自己的隔离)
actor BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
}
// 更好:MainActor类(如果不需要并发访问)
@MainActor
class BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
}
从Combine到AsyncSequence
// 之前:Combine发布者
cancellable = NotificationCenter.default
.publisher(for: .userDidLogin)
.sink { notification in /* ... */ }
// 之后:AsyncSequence
for await _ in NotificationCenter.default.notifications(named: .userDidLogin) {
// 处理通知
}
快速参考
| 关键字 | 目的 |
|---|---|
async |
函数可以挂起 |
await |
挂起点 |
Task { } |
启动异步工作,继承隔离 |
Task.detached { } |
启动异步工作,无继承 |
@MainActor |
在主线程上运行 |
actor |
具有隔离可变状态的类型 |
nonisolated |
选择退出actor隔离 |
nonisolated(nonsending) |
继承调用者的隔离 |
@concurrent |
始终在后台运行(Swift 6.2+) |
Sendable |
安全跨越隔离边界 |
sending |
非Sendable类型的单向传输 |
async let |
启动并行工作 |
TaskGroup |
动态并行工作 |
易用并发设置(Swift 6.2+)
对于新的Xcode 26+项目,这些默认启用:
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor
SWIFT_APPROACHABLE_CONCURRENCY = YES
效果:
- 一切在MainActor上运行,除非显式标记其他
nonisolated async函数保持在调用者的actor上,而不是跳转到后台- Sendable错误变得更罕见
资源
详细技术参考,请查阅:
references/fundamentals.md- async/await、Tasks、结构化并发references/isolation.md- Actors、MainActor、隔离域、继承references/sendable.md- Sendable协议、非Sendable模式、隔离参数references/common-mistakes.md- 详细避免示例references/glossary.md- 完整术语参考
参考搜索模式:
- 隔离:
grep -i "isolation\|actor\|mainactor\|nonisolated" - Sendable:
grep -i "sendable\|sending\|boundary" - Tasks:
grep -i "task\|taskgroup\|async let\|structured"