name: swift-concurrency-developer description: 使用 Office Building 心智模型提供 Swift 并发编程的专家指导。适用于处理 actors、isolation、Sendable、TaskGroups,或修复并发警告和数据竞争问题。
Swift 并发开发者(智能路由器)
目的
使用来自 Fucking Approachable Swift Concurrency 的 “Office Building” 心智模型,结合 Swift Concurrency Course 的综合参考资料,提供 Swift 并发系统的专家指导。
自动激活时机
- 处理 actors、isolation、Sendable、TaskGroups
- 关键词:
actor、isolation、Sendable、TaskGroup、nonisolated、async let - 修复并发警告或数据竞争问题
代理行为合约(遵循这些规则)
- 分析项目/包文件以找出 Swift 语言模式(Swift 5.x vs Swift 6)和使用的 Xcode/Swift 工具链,当建议依赖于此时。
- 在提出修复之前,识别隔离边界:
@MainActor、自定义 actor、actor 实例隔离或 nonisolated。 - 不要推荐
@MainActor作为通用修复。证明为什么主 actor 隔离适用于代码。 - 优先选择结构化并发(子任务、任务组)而非非结构化任务。仅在明确原因下使用
Task.detached。 - 如果推荐
@preconcurrency、@unchecked Sendable或nonisolated(unsafe),要求:- 记录安全不变式
- 后续工单以移除或迁移它
- 对于迁移工作,优化最小影响范围(小、可评审的更改)并添加验证步骤。
- 课程参考资料仅用于深入学习。仅在它们明显帮助回答开发者问题时使用。
项目设置发现
分析 Swift 项目的并发问题时:
-
项目设置发现
- 使用
Read在Package.swift上获取 SwiftPM 设置(工具版本、严格并发标志、即将推出的功能) - 使用
Grep在.pbxproj文件中查找SWIFT_STRICT_CONCURRENCY或SWIFT_DEFAULT_ACTOR_ISOLATION
- 使用
-
手动检查
- SwiftPM:检查
Package.swift中的.enableExperimentalFeature("StrictConcurrency=targeted")或类似内容 - Xcode 项目:在
project.pbxproj中搜索SWIFT_DEFAULT_ACTOR_ISOLATION、SWIFT_STRICT_CONCURRENCY
- SwiftPM:检查
核心心智模型:办公楼
将您的应用视为一座办公楼,其中 隔离域 是带锁的私人办公室:
| 概念 | 办公楼类比 | Swift |
|---|---|---|
| MainActor | 前台(处理所有 UI) | @MainActor |
| actor | 部门办公室(会计、法律) | actor BankAccount { } |
| nonisolated | 走廊(共享空间) | nonisolated func name() |
| Sendable | 复印件(安全分享) | struct User: Sendable |
| Non-Sendable | 原始文档(留在一个办公室) | class Counter { } |
关键洞见:您不能闯入他人的办公室。您敲门(await)并等待。
快速决策树
当开发者需要并发指导时:
-
从零开始异步代码?
- 阅读
references/async-await-basics.md获取基础模式 - 对于并行操作 →
references/tasks.md(async let、任务组)
- 阅读
-
保护共享可变状态?
- 需要保护基于类的状态 →
references/actors.md(actors、@MainActor) - 需要线程安全值传递 →
references/sendable.md(Sendable 一致性)
- 需要保护基于类的状态 →
-
管理异步操作?
- 结构化异步工作 →
references/tasks.md(Task、子任务、取消) - 流式数据 →
references/async-sequences.md(AsyncSequence、AsyncStream)
- 结构化异步工作 →
-
处理遗留框架?
- Core Data 集成 →
references/core-data.md - 通用迁移 →
references/migration.md
- Core Data 集成 →
-
性能或调试问题?
- 慢速异步代码 →
references/performance.md(性能分析、暂停点) - 测试问题 →
references/testing.md(XCTest、Swift Testing)
- 慢速异步代码 →
-
理解线程行为?
- 阅读
references/threading.md获取线程/任务关系和隔离
- 阅读
-
任务内存问题?
- 阅读
references/memory-management.md获取防循环引用模式
- 阅读
优先处理手册(常见错误 -> 下一步最佳操作)
- SwiftLint 并发相关警告
- 使用
references/linting.md获取规则意图和首选修复;避免使用虚拟 awaits 作为 “修复”。
- 使用
- “发送非 Sendable 类型的值…可能引发数据竞争”
- 首先:识别值跨隔离边界的位置
- 然后:使用
references/sendable.md和references/threading.md
- “主 actor 隔离…无法从非隔离上下文使用”
- 首先:决定它是否真正属于
@MainActor - 然后:使用
references/actors.md(全局 actors、nonisolated、隔离参数)
- 首先:决定它是否真正属于
- XCTest 异步错误,如 “wait(…) 在异步上下文中不可用”
- 使用
references/testing.md(await fulfillment(of:)和 Swift Testing 模式)
- 使用
- Core Data 并发警告/错误
- 使用
references/core-data.md(DAO/NSManagedObjectID、默认隔离冲突)
- 使用
快速模式
异步/等待
func fetchUser(id: Int) async throws -> User {
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
使用 async let 并行工作
async let avatar = fetchImage("avatar.jpg")
async let banner = fetchImage("banner.jpg")
return Profile(avatar: try await avatar, banner: try await banner)
任务
// SwiftUI - 当视图消失时取消
.task { avatar = await downloadAvatar() }
// 手动任务(继承 actor 上下文)
Task { await saveProfile() }
任务组用于动态并行工作
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { avatar = try await downloadAvatar() }
group.addTask { bio = try await fetchBio() }
try await group.waitForAll()
}
Actors
actor BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) { balance += amount }
// 不需要 await - 在 actor 内部可直接访问
nonisolated func bankName() -> String { "Acme Bank" }
}
await account.deposit(100) // 从外部必须 await
let name = account.bankName() // 不需要 await
Sendable 类型
// 自动 Sendable - 值类型
struct User: Sendable {
let id: Int
let name: String
}
// 具有内部同步的线程安全类
final class ThreadSafeCache: @unchecked Sendable {
private let lock = NSLock()
private var storage: [String: Data] = [:]
}
常见错误
1. 认为异步 = 后台
// 错误:仍阻塞主线程!
@MainActor func slowFunction() async {
let result = expensiveCalculation() // 同步 = 阻塞
}
// 正确:使用分离任务处理 CPU 密集型工作
Task.detached(priority: .userInitiated) {
let result = expensiveCalculation()
await MainActor.run { updateUI(result) }
}
2. 创建过多 actors
大多数内容可以存在于 MainActor 上。仅当有无法在 MainActor 上的共享可变状态时才创建 actors。
3. 不必要地使用 MainActor.run
// 错误
await MainActor.run { self.data = data }
// 正确 - 注释函数
@MainActor func loadData() async { self.data = await fetchData() }
4. 阻塞协作线程池(违反运行时合约)
永远不要在异步代码中使用 DispatchSemaphore、DispatchGroup.wait() 或条件变量。
为什么:这些原语对运行时隐藏依赖。协作线程池有一个合约,即线程总是会向前推进。阻塞原语违反此合约并可能导致死锁。
// ❌ 危险:可能导致协作池死锁
let semaphore = DispatchSemaphore(value: 0)
Task {
await doWork()
semaphore.signal()
}
semaphore.wait() // 线程阻塞,运行时不知情
// ✅ 使用 async/await 替代
let result = await doWork()
调试提示:设置 LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 以在开发期间捕获阻塞调用。
5. 创建不必要的任务
// 错误 - 非结构化
Task { await fetchUsers() }
Task { await fetchPosts() }
// 正确 - 结构化并发
async let users = fetchUsers()
async let posts = fetchPosts()
await (users, posts)
6. 使所有内容都为 Sendable
并非所有内容都需要跨边界。询问数据是否真的在隔离域之间移动。
7. 不批处理 MainActor 跳转
主线程独立于协作线程池。每次跳转到/从 MainActor 都需要完整的上下文切换。
// ❌ 多次上下文切换
for item in items {
let processed = await processItem(item)
await MainActor.run { displayItem(processed) } // 每次项目上下文切换
}
// ✅ 单次上下文切换
let processed = await processAllItems(items)
await MainActor.run {
for item in processed { displayItem(item) }
}
快速参考
| 关键词 | 目的 |
|---|---|
async |
函数可以暂停 |
await |
在此暂停直到完成 |
Task { } |
启动异步工作,继承上下文 |
Task.detached { } |
启动异步工作,无上下文 |
@MainActor |
在主线程上运行 |
actor |
具有隔离可变状态的类型 |
nonisolated |
退出 actor 隔离 |
Sendable |
安全在隔离域之间传递 |
@unchecked Sendable |
信任我,它是线程安全的 |
async let |
启动并行工作 |
TaskGroup |
动态并行工作 |
当编译器抱怨时
跟踪隔离:它从哪里来?代码试图在哪里运行?什么数据跨边界?
一旦问对问题,答案通常显而易见。
参考文件
根据需要加载这些文件以处理特定主题:
基础概念
async-await-basics.md- async/await 语法、执行顺序、async let、URLSession 模式tasks.md- 任务生命周期、取消、优先级、任务组、结构化 vs 非结构化threading.md- 线程/任务关系、暂停点、隔离域、nonisolatedglossary.md- 核心并发术语的快速定义
隔离与安全
actors.md- Actor 隔离、@MainActor、全局 actors、重入、自定义执行器、Mutexsendable.md- Sendable 一致性、值/引用类型、@unchecked、区域隔离memory-management.md- 任务中的循环引用、内存安全模式
高级模式
async-sequences.md- AsyncSequence、AsyncStream、何时使用 vs 常规异步方法async-algorithms.md- Swift Async Algorithms 包、组合序列task-local-values.md- 任务本地上下文传播、跟踪、日志模式core-data.md- NSManagedObject sendability、自定义执行器、隔离冲突
质量与迁移
performance.md- 使用 Instruments 性能分析、减少暂停点、执行策略testing.md- XCTest 异步模式、Swift Testing、并发测试工具migration.md- Swift 6 迁移策略、闭包到异步转换、@preconcurrencylinting.md- 并发焦点 lint 规则和 SwiftLint async_without_await
项目特定参考(Anytype)
approachable-concurrency.md- 易用并发快速指南swift-6-2-concurrency.md- Swift 6.2 并发更新(未来参考)swiftui-concurrency-tour.md- SwiftUI 特定并发模式
最佳实践总结
- 优先选择结构化并发 - 尽可能使用任务组而非非结构化任务
- 最小化暂停点 - 保持 actor 隔离部分小以减少上下文切换
- 明智使用 @MainActor - 仅用于真正与 UI 相关的代码
- 使类型为 Sendable - 通过符合 Sendable 启用安全并发访问
- 处理取消 - 在长时间运行操作中检查 Task.isCancelled
- 避免阻塞 - 永远不要在异步上下文中使用信号量或锁(违反运行时合约)
- 测试并发代码 - 使用适当的异步测试方法并考虑时序问题
- 批处理 MainActor 跳转 - 分组 UI 更新以最小化跳转到/从主线程的上下文切换
- 理解运行时合约 - 线程必须总是向前推进;使用安全原语
- 使用 LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 - 调试环境变量以捕获阻塞调用
验证清单(当更改并发代码时)
- 在解释诊断之前,确认构建设置(默认隔离、严格并发、即将推出的功能)。
- 重构后:
- 运行测试,特别是并发敏感测试(见
references/testing.md)。 - 如果与性能相关,使用 Instruments 验证(见
references/performance.md)。 - 如果与生命周期相关,验证 deinit/取消行为(见
references/memory-management.md)。
- 运行测试,特别是并发敏感测试(见
进一步阅读
- Fucking Approachable Swift Concurrency - Office Building 心智模型
- Swift Concurrency Course - 综合参考(Antoine van der Lee)
- WWDC21: Swift Concurrency: Behind the Scenes - 运行时内部、线程模型、运行时合约
- WWDC23: Beyond the basics of structured concurrency - 任务组、取消处理器、任务本地
- Matt Massicotte’s Blog - 并发深度探讨
- Swift Concurrency Documentation
相关技能与文档
- ios-dev-guidelines →
IOS_DEVELOPMENT_GUIDE.md- 通用 Swift/iOS 模式、MVVM、协调器 - tests-developer → 使用 Swift Testing 框架测试异步代码
- swiftui-performance-developer → SwiftUI 视图中的性能优化
导航:此技能提供并发心智模型。对于通用 Swift/iOS 模式,见 ios-dev-guidelines。
归属:Office Building 心智模型来自 Dimillian/Skills。参考文件来自 AvdLee/Swift-Concurrency-Agent-Skill。