Swift并发编程技能Skill swift-concurrency

Swift 并发编程技能是用于指导使用现代并发模式(如 async/await、Tasks、actors、MainActor)编写线程安全 Swift 代码的实用指南。它帮助开发者构建新异步代码、审计现有代码以发现并发问题,以及将传统回调或 Combine 代码重构为结构化并发。适用于 iOS 和 macOS 应用开发,提升代码性能和可维护性。关键词:Swift并发编程,async/await,actors,MainActor,结构化并发,移动开发,线程安全,并发审计,重构技巧。

移动开发 0 次安装 0 次浏览 更新于 3/17/2026

名称: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工作仍然阻塞
  • [ ] 在结构化可行时使用非结构化TasksTask { }而不是async letTaskGroup
  • [ ] 缺少取消处理:长操作应该检查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"