关于 swift-concurrency
swift-concurrency 为 Swift 开发者提供编写、审查和改造线程安全代码的指导,涵盖 async/await、隔离与任务协调,也介绍从回调、队列和 Combine 迁移。
主要功能
- 01async Swift 代码编写指导
- 02并发问题审查清单
- 03隔离与 Sendable 实践
- 04结构化并行任务模式
- 05SwiftUI 并发指导
- 06改造旧式并发代码
适用场景
- 01设计新的异步 API
- 02检查代码中的数据竞争
- 03将回调改为 async/await
- 04将队列式代码改为 actor
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ jamesrochabrun/ skills --skill swift-concurrency安装后在 Claude Code 中输入/swift-concurrency确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/common-mistakes.mdfundamentals.mdglossary.mdisolation.mdsendable.md详细介绍
Swift 并发
概览
本技能提供使用现代并发模式编写线程安全 Swift 代码的指导,涵盖三种主要工作流:编写新的异步代码、审查现有代码中的问题,以及将旧模式重构为 Swift 6+。
核心原则:默认继承隔离。使用易用并发时,代码会从 MainActor 开始,并自动在程序中传播。需要时可显式退出隔离。
工作流决策树
What are you doing?
│
├─► BUILDING new async code
│ └─► See "Building Workflow" below
│
├─► AUDITING existing code
│ └─► See "Auditing Checklist" below
│
└─► REFACTORING legacy code
└─► See "Refactoring Workflow" below
编写工作流
编写新的异步代码时,请遵循以下决策流程:
步骤 1:确定隔离需求
Does this type manage UI state or interact with UI?
│
├─► YES → Mark with @MainActor
│
└─► NO → Does it have mutable state shared across contexts?
│
├─► YES → Consider: Can it live on MainActor anyway?
│ │
│ ├─► YES → Use @MainActor (simpler)
│ │
│ └─► NO → Use a custom actor (requires justification)
│
└─► NO → Leave non-isolated (default with Approachable Concurrency)
步骤 2:设计异步函数
// PREFER: Inherit caller's isolation (works everywhere)
func fetchData(isolation: isolated (any Actor)? = #isolation) async throws -> Data {
// Runs on whatever actor the caller is on
}
// USE WHEN: CPU-intensive work that must run in background
@concurrent
func processLargeFile() async -> Result { }
// AVOID: Non-isolated async without explicit choice
func ambiguousAsync() async { } // Where does this run?
步骤 3:处理并行工作
// For known number of independent operations
async let avatar = fetchImage("avatar.jpg")
async let banner = fetchImage("banner.jpg")
let (a, b) = await (avatar, banner)
// For dynamic number of operations
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() } // Auto-cancels on disappear
.task(id: userID) { /* Reloads when userID changes */ }
}
}
// For user actions
Button("Save") {
Task { await saveProfile() } // Inherits MainActor isolation
}
审查清单
审查 Swift 并发代码时,请检查以下问题:
严重问题(必须修复)
- 阻塞协作式线程池:检查异步上下文中是否有
DispatchSemaphore.wait()、DispatchGroup.wait()或类似阻塞调用 - 数据竞争:非 Sendable 类型跨越隔离边界时未妥善处理
- 非 Sendable 类型中的非隔离异步函数:这类函数只能在非隔离上下文中正常工作
常见问题(应修复)
- 过度使用 Actor:没有充分理由就使用自定义 Actor(参见参考资料中的“Actor 合理性测试”)
- 不必要的
MainActor.run:通常应改为在函数上使用@MainActor - 误以为 async 就代表后台运行:异步函数中的同步 CPU 工作仍会造成阻塞
- 有结构化并发可用却使用非结构化 Task:用
Task { }代替async let或TaskGroup - 未处理取消:长时间运行的操作应检查
Task.isCancelled
SwiftUI 特有问题
- 视图未隔离到 MainActor:SwiftUI 视图应使用
@MainActor(或使用@Observable) - 从脱离任务中访问 @State:必须切回 MainActor
Sendable 合规性
- 过度使用 @unchecked Sendable:应少见且有充分理由
- 将所有类型都设为 Sendable:并非所有类型都需要跨越隔离边界
- 逃逸的非 Sendable 闭包:检查闭包捕获
重构工作流
从回调迁移到 async/await
// BEFORE: Callback-based
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()
}
// AFTER: async/await with continuation
func fetchUser(id: Int) async throws -> User {
try await withCheckedThrowingContinuation { continuation in
fetchUser(id: id) { result in
continuation.resume(with: result)
}
}
}
从 DispatchQueue 迁移到 Actors
// BEFORE: Queue-based protection
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 }
}
}
// AFTER: Actor (if truly needs own isolation)
actor BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
}
// BETTER: MainActor class (if doesn't need concurrent access)
@MainActor
class BankAccount {
var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
}
从 Combine 迁移到 AsyncSequence
// BEFORE: Combine publisher
cancellable = NotificationCenter.default
.publisher(for: .userDidLogin)
.sink { notification in /* ... */ }
// AFTER: AsyncSequence
for await _ in NotificationCenter.default.notifications(named: .userDidLogin) {
// Handle notification
}
快速参考
| 关键字 | 用途 |
|---|---|
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、Task、结构化并发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" - Task:
grep -i "task\|taskgroup\|async let\|structured"
评论
支持 Markdown