swift-concurrency

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Swift Concurrency

Swift 并发编程

Overview

概述

This skill provides guidance for writing thread-safe Swift code using modern concurrency patterns. It covers three main workflows: building new async code, auditing existing code for issues, and refactoring legacy patterns to Swift 6+.
Core principle: Isolation is inherited by default. With Approachable Concurrency, code starts on MainActor and propagates through the program automatically. Opt out explicitly when needed.
本指南提供了使用现代并发模式编写线程安全Swift代码的指导,涵盖三个主要工作流:构建新的异步代码、审计现有代码的问题,以及将传统模式重构为Swift 6+版本的代码。
核心原则:默认继承隔离域。开启Approachable Concurrency后,代码默认在MainActor上运行,并自动在程序中传播隔离域。必要时可显式退出隔离。

Workflow Decision Tree

工作流决策树

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
你正在进行什么操作?
├─► 构建新的异步代码
│   └─► 查看下方「构建工作流」
├─► 审计现有代码
│   └─► 查看下方「审计检查清单」
└─► 重构传统代码
    └─► 查看下方「重构工作流」

Building Workflow

构建工作流

When writing new async code, follow this decision process:
编写新的异步代码时,请遵循以下决策流程:

Step 1: Determine Isolation Needs

步骤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)
该类型是否管理UI状态或与UI交互?
├─► 是 → 使用@MainActor标记
└─► 否 → 它是否包含跨上下文共享的可变状态?
         ├─► 是 → 考虑:是否可以直接在MainActor上运行?
         │         │
         │         ├─► 是 → 使用@MainActor(实现更简单)
         │         │
         │         └─► 否 → 使用自定义actor(需有合理理由)
         └─► 否 → 保持非隔离状态(开启Approachable Concurrency后的默认设置)

Step 2: Design Async Functions

步骤2:设计异步函数

swift
// 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?
swift
// 推荐:继承调用者的隔离域(适用于所有场景)
func fetchData(isolation: isolated (any Actor)? = #isolation) async throws -> Data {
  // 在调用者所在的任意actor上运行
}

// 适用场景:必须在后台运行的CPU密集型任务
@concurrent
func processLargeFile() async -> Result { }

// 避免:未显式指定的非隔离异步函数
func ambiguousAsync() async { } // 该函数会在何处运行?

Step 3: Handle Parallel Work

步骤3:处理并行任务

swift
// 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()
}
swift
// 处理已知数量的独立操作
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()
}

Step 4: SwiftUI Integration

步骤4:SwiftUI 集成

swift
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
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隔离域
}

Auditing Checklist

审计检查清单

When reviewing Swift concurrency code, check for these issues:
审核Swift并发代码时,请检查以下问题:

Critical Issues (Must Fix)

严重问题(必须修复)

  • Blocking the cooperative pool: Look for
    DispatchSemaphore.wait()
    ,
    DispatchGroup.wait()
    , or similar blocking calls inside async contexts
  • Data races: Non-Sendable types crossing isolation boundaries without proper handling
  • Non-isolated async in non-Sendable types: These only work from non-isolated contexts
  • 阻塞协作池:检查异步上下文中是否存在
    DispatchSemaphore.wait()
    DispatchGroup.wait()
    或类似的阻塞调用
  • 数据竞争:非Sendable类型未经过适当处理就跨隔离域传递
  • 非Sendable类型中的非隔离异步函数:这类函数仅能在非隔离上下文中运行

Common Issues (Should Fix)

常见问题(建议修复)

  • Actor overuse: Custom actors without justification (see "Actor Justification Test" in references)
  • Unnecessary
    MainActor.run
    : Should usually be
    @MainActor
    on the function instead
  • Thinking async = background: Synchronous CPU work inside async functions still blocks
  • Unstructured Tasks where structured works:
    Task { }
    instead of
    async let
    or
    TaskGroup
  • Missing cancellation handling: Long operations should check
    Task.isCancelled
  • Actor过度使用:无合理理由使用自定义actor(参考参考资料中的「Actor合理性测试」)
  • 不必要的
    MainActor.run
    :通常应改为在函数上标记
    @MainActor
  • 误认为异步等于后台运行:异步函数中的同步CPU密集型任务仍会阻塞线程
  • 在可使用结构化任务的场景使用非结构化任务:使用
    Task { }
    而非
    async let
    TaskGroup
  • 缺少取消处理:长时间运行的操作应检查
    Task.isCancelled
    状态

SwiftUI-Specific

SwiftUI 专属问题

  • Views not MainActor-isolated: SwiftUI views should be
    @MainActor
    (or use
    @Observable
    )
  • Accessing @State from detached tasks: Must hop back to MainActor
  • 视图未标记MainActor隔离:SwiftUI视图应标记
    @MainActor
    (或使用
    @Observable
  • 在分离任务中访问@State:必须切换回MainActor才能访问

Sendable Compliance

Sendable 合规性检查

  • @unchecked Sendable overuse: Should be rare and justified
  • Making everything Sendable: Not all types need to cross boundaries
  • Non-Sendable closures escaping: Check closure captures
  • 过度使用@unchecked Sendable:这类用法应极少且有合理理由
  • 将所有类型都标记为Sendable:并非所有类型都需要跨隔离域传递
  • 非Sendable闭包逃逸:检查闭包的捕获内容

Refactoring Workflow

重构工作流

From Callbacks to async/await

从回调函数迁移到async/await

swift
// 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)
    }
  }
}
swift
// 重构前:基于回调的实现
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)
    }
  }
}

From DispatchQueue to Actors

从DispatchQueue迁移到Actors

swift
// 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
  }
}
swift
// 重构前:基于队列的状态保护
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
  }
}

From Combine to AsyncSequence

从Combine迁移到AsyncSequence

swift
// BEFORE: Combine publisher
cancellable = NotificationCenter.default
  .publisher(for: .userDidLogin)
  .sink { notification in /* ... */ }

// AFTER: AsyncSequence
for await _ in NotificationCenter.default.notifications(named: .userDidLogin) {
  // Handle notification
}
swift
// 重构前:Combine发布者
cancellable = NotificationCenter.default
  .publisher(for: .userDidLogin)
  .sink { notification in /* ... */ }

// 重构后:AsyncSequence
for await _ in NotificationCenter.default.notifications(named: .userDidLogin) {
  // 处理通知
}

Quick Reference

速查参考

KeywordPurpose
async
Function can suspend
await
Suspension point
Task { }
Start async work, inherits isolation
Task.detached { }
Start async work, no inheritance
@MainActor
Runs on main thread
actor
Type with isolated mutable state
nonisolated
Opts out of actor isolation
nonisolated(nonsending)
Inherits caller's isolation
@concurrent
Always run on background (Swift 6.2+)
Sendable
Safe to cross isolation boundaries
sending
One-way transfer of non-Sendable
async let
Start parallel work
TaskGroup
Dynamic parallel work
关键字用途
async
函数可挂起
await
挂起点
Task { }
启动异步任务,继承隔离域
Task.detached { }
启动异步任务,不继承隔离域
@MainActor
在主线程运行
actor
拥有隔离可变状态的类型
nonisolated
退出actor隔离
nonisolated(nonsending)
继承调用者的隔离域
@concurrent
始终在后台运行(Swift 6.2+)
Sendable
可安全跨隔离域传递
sending
非Sendable类型的单向传递
async let
启动并行任务
TaskGroup
动态并行任务

Approachable Concurrency Settings (Swift 6.2+)

Approachable Concurrency 设置(Swift 6.2+)

For new Xcode 26+ projects, these are enabled by default:
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor
SWIFT_APPROACHABLE_CONCURRENCY = YES
Effects:
  • Everything runs on MainActor unless explicitly marked otherwise
  • nonisolated async
    functions stay on caller's actor instead of hopping to background
  • Sendable errors become much rarer
在Xcode 26+的新项目中,以下设置默认启用:
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor
SWIFT_APPROACHABLE_CONCURRENCY = YES
效果:
  • 所有代码默认在MainActor上运行,除非显式标记其他隔离域
  • nonisolated async
    函数会保留在调用者的actor上,而非切换到后台
  • Sendable相关错误会大幅减少

Resources

参考资源

For detailed technical reference, consult:
  • references/fundamentals.md
    - async/await, Tasks, structured concurrency
  • references/isolation.md
    - Actors, MainActor, isolation domains, inheritance
  • references/sendable.md
    - Sendable protocol, non-Sendable patterns, isolated parameters
  • references/common-mistakes.md
    - Detailed examples of what to avoid
  • references/glossary.md
    - Complete terminology reference
Search patterns for references:
  • Isolation:
    grep -i "isolation\|actor\|mainactor\|nonisolated"
  • Sendable:
    grep -i "sendable\|sending\|boundary"
  • Tasks:
    grep -i "task\|taskgroup\|async let\|structured"
如需详细技术参考,请查阅:
  • 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"
  • 任务相关:
    grep -i "task\|taskgroup\|async let\|structured"