axiom-swift-concurrency-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Swift Concurrency API Reference

Swift并发API参考

Complete Swift concurrency API reference for copy-paste patterns and syntax lookup.
Complements
axiom-swift-concurrency
(which covers when and why to use concurrency — progressive journey, decision trees, @concurrent, isolated conformances).
Related skills:
axiom-swift-concurrency
(progressive journey, decision trees),
axiom-synchronization
(Mutex, locks),
axiom-assume-isolated
(assumeIsolated patterns)
这是一份完整的Swift并发API参考,提供可直接复制粘贴的代码模式和语法查询。
作为
axiom-swift-concurrency
的补充内容(后者涵盖了何时以及为何使用并发——循序渐进的指南、决策树、@concurrent、隔离式协议一致性)。
相关技能
axiom-swift-concurrency
(循序渐进指南、决策树)、
axiom-synchronization
(Mutex、锁)、
axiom-assume-isolated
(assumeIsolated模式)

Part 1: Actor Patterns

第一部分:Actor模式

Actor Definition

Actor定义

swift
actor ImageCache {
    private var cache: [URL: UIImage] = [:]

    func image(for url: URL) -> UIImage? {
        cache[url]
    }

    func store(_ image: UIImage, for url: URL) {
        cache[url] = image
    }
}

// Usage — must await across isolation boundary
let cache = ImageCache()
let image = await cache.image(for: url)
All properties and methods on an actor are isolated by default. Callers outside the actor's isolation domain must use
await
to access them.
swift
actor ImageCache {
    private var cache: [URL: UIImage] = [:]

    func image(for url: URL) -> UIImage? {
        cache[url]
    }

    func store(_ image: UIImage, for url: URL) {
        cache[url] = image
    }
}

// 使用方式——跨隔离边界必须使用await
let cache = ImageCache()
let image = await cache.image(for: url)
Actor的所有属性和方法默认都是隔离的。Actor隔离域外部的调用者必须使用
await
来访问它们。

Actor Isolation Rules

Actor隔离规则

Every actor's stored properties and methods are isolated to that actor. Access from outside the isolation boundary requires
await
, which suspends the caller until the actor can process the request.
swift
actor Counter {
    var count = 0              // Isolated — external access requires await
    let name: String           // let constants are implicitly nonisolated

    func increment() {         // Isolated — await required from outside
        count += 1
    }

    nonisolated func identity() -> String {
        name                   // OK: accessing nonisolated let
    }
}

let counter = Counter(name: "main")
await counter.increment()      // Must await across isolation boundary
let id = counter.identity()    // No await needed — nonisolated
每个Actor的存储属性和方法都与该Actor隔离。从隔离边界外部访问需要使用
await
,这会暂停调用者,直到Actor可以处理该请求。
swift
actor Counter {
    var count = 0              // 隔离属性——外部访问需要await
    let name: String           // let常量默认是非隔离的

    func increment() {         // 隔离方法——外部调用需要await
        count += 1
    }

    nonisolated func identity() -> String {
        name                   // 允许:访问非隔离的let属性
    }
}

let counter = Counter(name: "main")
await counter.increment()      // 跨隔离边界必须使用await
let id = counter.identity()    // 无需await——非隔离方法

nonisolated Keyword

nonisolated关键字

Opt out of isolation for synchronous access to non-mutable state.
swift
actor MyActor {
    let id: UUID               // let constants are implicitly nonisolated

    nonisolated var description: String {
        "Actor \(id)"          // Can only access nonisolated state
    }

    nonisolated func hash(into hasher: inout Hasher) {
        hasher.combine(id)     // Only nonisolated properties
    }
}
nonisolated
methods cannot access any isolated stored properties. Use this for protocol conformances (like
Hashable
,
CustomStringConvertible
) that require synchronous access.
退出隔离状态,以同步方式访问不可变状态。
swift
actor MyActor {
    let id: UUID               // let常量默认是非隔离的

    nonisolated var description: String {
        "Actor \(id)"          // 只能访问非隔离状态
    }

    nonisolated func hash(into hasher: inout Hasher) {
        hasher.combine(id)     // 仅能访问非隔离属性
    }
}
nonisolated
方法无法访问任何隔离的存储属性。可用于需要同步访问的协议一致性(如
Hashable
CustomStringConvertible
)。

Actor Reentrancy

Actor可重入性

Suspension points (
await
) inside an actor allow other callers to interleave. State may change between any two
await
expressions.
swift
actor BankAccount {
    var balance: Double = 0

    func transfer(amount: Double, to other: BankAccount) async {
        guard balance >= amount else { return }
        balance -= amount
        // REENTRANCY HAZARD: another caller could modify balance here
        // while we await the deposit on the other actor
        await other.deposit(amount)
    }

    func deposit(_ amount: Double) {
        balance += amount
    }
}
Pattern: Re-check state after every
await
inside an actor:
swift
actor BankAccount {
    var balance: Double = 0

    func transfer(amount: Double, to other: BankAccount) async -> Bool {
        guard balance >= amount else { return false }
        balance -= amount

        await other.deposit(amount)

        // Re-check invariants after await if needed
        return true
    }
}
Actor内部的挂起点(
await
)允许其他调用者插入执行。在任意两个
await
表达式之间,状态可能会发生变化。
swift
actor BankAccount {
    var balance: Double = 0

    func transfer(amount: Double, to other: BankAccount) async {
        guard balance >= amount else { return }
        balance -= amount
        // 可重入风险:在等待另一个Actor的存款操作时,其他调用者可能会修改balance
        await other.deposit(amount)
    }

    func deposit(_ amount: Double) {
        balance += amount
    }
}
解决方案:在Actor内部的每个
await
之后重新检查状态:
swift
actor BankAccount {
    var balance: Double = 0

    func transfer(amount: Double, to other: BankAccount) async -> Bool {
        guard balance >= amount else { return false }
        balance -= amount

        await other.deposit(amount)

        // 若有需要,在await后重新检查不变量
        return true
    }
}

Global Actors

全局Actor

A global actor provides a single shared isolation domain accessible from anywhere.
swift
@globalActor
actor MyGlobalActor {
    static let shared = MyGlobalActor()
}

@MyGlobalActor
func doWork() { /* isolated to MyGlobalActor */ }

@MyGlobalActor
class MyService {
    var state: Int = 0         // Isolated to MyGlobalActor
}
全局Actor提供一个单一的共享隔离域,可从任何位置访问。
swift
@globalActor
actor MyGlobalActor {
    static let shared = MyGlobalActor()
}

@MyGlobalActor
func doWork() { /* 隔离到MyGlobalActor */ }

@MyGlobalActor
class MyService {
    var state: Int = 0         // 隔离到MyGlobalActor
}

@MainActor

@MainActor

The built-in global actor for UI work. All UI updates must happen on
@MainActor
.
swift
@MainActor
class ViewModel: ObservableObject {
    @Published var items: [Item] = []

    func loadItems() async {
        let data = await fetchFromNetwork()
        items = data           // Safe: already on MainActor
    }
}

// Annotate individual members
class MixedService {
    @MainActor var uiState: String = ""

    @MainActor
    func updateUI() {
        uiState = "Done"
    }

    func backgroundWork() async -> String {
        await heavyComputation()
    }
}
Subclass inheritance: If a class is
@MainActor
, all subclasses inherit that isolation.
用于UI工作的内置全局Actor。所有UI更新都必须在
@MainActor
上执行。
swift
@MainActor
class ViewModel: ObservableObject {
    @Published var items: [Item] = []

    func loadItems() async {
        let data = await fetchFromNetwork()
        items = data           // 安全:已处于MainActor上下文
    }
}

// 为单个成员添加注解
class MixedService {
    @MainActor var uiState: String = ""

    @MainActor
    func updateUI() {
        uiState = "Done"
    }

    func backgroundWork() async -> String {
        await heavyComputation()
    }
}
子类继承:如果一个类标记为
@MainActor
,所有子类都会继承该隔离属性。

Actor Init

Actor初始化

Actor initializers are NOT isolated to the actor. You cannot call isolated methods from init.
swift
actor DataManager {
    var data: [String] = []

    init() {
        // Cannot call isolated methods here
        // self.loadDefaults()  // ERROR: actor-isolated method in non-isolated init
    }

    // Use a factory method instead
    static func create() async -> DataManager {
        let manager = DataManager()
        await manager.loadDefaults()
        return manager
    }

    func loadDefaults() {
        data = ["default"]
    }
}
Actor的初始化方法与Actor隔离。你无法在init中调用隔离方法。
swift
actor DataManager {
    var data: [String] = []

    init() {
        // 此处无法调用隔离方法
        // self.loadDefaults()  // 错误:在非隔离的init中调用Actor隔离方法
    }

    // 改用工厂方法
    static func create() async -> DataManager {
        let manager = DataManager()
        await manager.loadDefaults()
        return manager
    }

    func loadDefaults() {
        data = ["default"]
    }
}

Actor Gotcha Table

Actor问题排查表

GotchaSymptomFix
Actor reentrancyState changes between awaitsRe-check state after each await
nonisolated accessing isolated stateCompiler errorRemove nonisolated or make property nonisolated
Calling actor method from sync context"Expression is 'async'"Wrap in Task {} or make caller async
Global actor inheritanceSubclass inherits @MainActorBe intentional about which methods need isolation
Actor init not isolatedCan't call isolated methods in initUse factory method or populate after init
Actor protocol conformance"Non-isolated" conformance errorUse nonisolated for protocol methods, or isolated conformance (Swift 6.2+)

问题症状修复方案
Actor可重入await之间状态发生变化每个await后重新检查状态
nonisolated方法访问隔离状态编译器错误移除nonisolated标记,或将属性改为非隔离
从同步上下文调用Actor方法"Expression is 'async'"错误用Task {}包裹,或让调用者改为async
全局Actor继承子类继承@MainActor明确哪些方法需要隔离
Actor init不隔离无法在init中调用隔离方法使用工厂方法,或在init后填充数据
Actor协议一致性"Non-isolated"一致性错误为协议方法使用nonisolated,或使用隔离式一致性(Swift 6.2+)

Part 2: Sendable Patterns

第二部分:Sendable模式

Automatic Sendable Conformance

自动Sendable一致性

Value types are Sendable when all stored properties are Sendable.
swift
// Structs: Sendable when all stored properties are Sendable
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

// Enums: Sendable when all associated values are Sendable
enum LoadState: Sendable {
    case idle
    case loading
    case loaded(String)        // String is Sendable
    case failed(Error)         // ERROR: Error is not Sendable
}

// Fix: use a Sendable error type
enum LoadState: Sendable {
    case idle
    case loading
    case loaded(String)
    case failed(any Error & Sendable)
}
当所有存储属性都是Sendable时,值类型自动符合Sendable。
swift
// 结构体:所有存储属性为Sendable时,结构体自动符合Sendable
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

// 枚举:所有关联值为Sendable时,枚举自动符合Sendable
enum LoadState: Sendable {
    case idle
    case loading
    case loaded(String)        // String是Sendable
    case failed(Error)         // 错误:Error不符合Sendable
}

// 修复方案:使用符合Sendable的错误类型
enum LoadState: Sendable {
    case idle
    case loading
    case loaded(String)
    case failed(any Error & Sendable)
}

@Sendable Closures

@Sendable闭包

Closures passed across isolation boundaries must be
@Sendable
. A
@Sendable
closure cannot capture mutable local state.
swift
func runInBackground(_ work: @Sendable () -> Void) {
    Task.detached { work() }
}

// All captured values must be Sendable
var count = 0
runInBackground {
    // ERROR: capture of mutable local variable
    // count += 1
}

let snapshot = count
runInBackground {
    print(snapshot)            // OK: let binding of Sendable type
}
跨隔离边界传递的闭包必须标记为
@Sendable
@Sendable
闭包无法捕获可变的本地状态。
swift
func runInBackground(_ work: @Sendable () -> Void) {
    Task.detached { work() }
}

// 所有捕获的值必须是Sendable
var count = 0
runInBackground {
    // 错误:捕获可变本地变量
    // count += 1
}

let snapshot = count
runInBackground {
    print(snapshot)            // 允许:捕获Sendable类型的let绑定
}

@unchecked Sendable

@unchecked Sendable

Manual guarantee of thread safety. Use only when you provide synchronization yourself.
swift
final class ThreadSafeCache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Any] = [:]

    func get(_ key: String) -> Any? {
        lock.lock()
        defer { lock.unlock() }
        return storage[key]
    }

    func set(_ key: String, value: Any) {
        lock.lock()
        defer { lock.unlock() }
        storage[key] = value
    }
}
Requirements for @unchecked Sendable:
  • Class must be
    final
  • All mutable state must be protected by a synchronization primitive (lock, queue, Mutex)
  • You are responsible for correctness — the compiler will not check
手动保证线程安全。仅当你自己提供同步机制时使用。
swift
final class ThreadSafeCache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Any] = [:]

    func get(_ key: String) -> Any? {
        lock.lock()
        defer { lock.unlock() }
        return storage[key]
    }

    func set(_ key: String, value: Any) {
        lock.lock()
        defer { lock.unlock() }
        storage[key] = value
    }
}
@unchecked Sendable的要求
  • 类必须是
    final
  • 所有可变状态必须由同步原语(锁、队列、Mutex)保护
  • 你需要对正确性负责——编译器不会进行检查

Conditional Conformance

条件一致性

swift
struct Box<T> {
    let value: T
}

// Box is Sendable only when T is Sendable
extension Box: Sendable where T: Sendable {}

// Standard library uses this extensively:
// Array<Element>: Sendable where Element: Sendable
// Dictionary<Key, Value>: Sendable where Key: Sendable, Value: Sendable
// Optional<Wrapped>: Sendable where Wrapped: Sendable
swift
struct Box<T> {
    let value: T
}

// 仅当T是Sendable时,Box才符合Sendable
extension Box: Sendable where T: Sendable {}

// 标准库广泛使用此模式:
// Array<Element>: Sendable where Element: Sendable
// Dictionary<Key, Value>: Sendable where Key: Sendable, Value: Sendable
// Optional<Wrapped>: Sendable where Wrapped: Sendable

sending Parameter Modifier (SE-0430)

sending参数修饰符(SE-0430)

Transfer ownership of a value across isolation boundaries. The caller gives up access.
swift
func process(_ value: sending String) async {
    // Caller can no longer access value after this call
    await store(value)
}

// Useful for transferring non-Sendable types when caller won't use them again
func handOff(_ connection: sending NetworkConnection) async {
    await manager.accept(connection)
}
跨隔离边界转移值的所有权。调用者将失去对该值的访问权限。
swift
func process(_ value: sending String) async {
    // 调用者在此调用后无法再访问value
    await store(value)
}

// 当调用者不再需要使用非Sendable类型时,可用于转移所有权
func handOff(_ connection: sending NetworkConnection) async {
    await manager.accept(connection)
}

Build Settings

构建设置

Control the strictness of Sendable checking in Xcode:
SettingValueBehavior
SWIFT_STRICT_CONCURRENCY
minimal
Only explicit Sendable annotations checked
SWIFT_STRICT_CONCURRENCY
targeted
Inferred Sendable + closure checking
SWIFT_STRICT_CONCURRENCY
complete
Full strict concurrency (Swift 6 default)
在Xcode中控制Sendable检查的严格程度:
设置项行为
SWIFT_STRICT_CONCURRENCY
minimal
仅检查显式的Sendable注解
SWIFT_STRICT_CONCURRENCY
targeted
检查推断的Sendable和闭包
SWIFT_STRICT_CONCURRENCY
complete
完全严格的并发检查(Swift 6默认)

Sendable Gotcha Table

Sendable问题排查表

GotchaSymptomFix
Class can't be Sendable"Class cannot conform to Sendable"Make final + immutable, or @unchecked Sendable with locks
Closure captures non-Sendable"Capture of non-Sendable type"Copy value before capture, or make type Sendable
Protocol can't require SendableGeneric constraints complexUse
where T: Sendable
@unchecked Sendable hides bugsData races at runtimeOnly use when lock/queue guarantees safety
Array/Dictionary conditionalCollection is Sendable only if Element isEnsure element types are Sendable
Error not Sendable"Type does not conform to Sendable"Use
any Error & Sendable
or typed errors

问题症状修复方案
类无法符合Sendable"Class cannot conform to Sendable"错误将类设为final且不可变,或使用带锁的@unchecked Sendable
闭包捕获非Sendable类型"Capture of non-Sendable type"错误捕获前复制值,或让类型符合Sendable
协议无法要求Sendable泛型约束复杂使用
where T: Sendable
@unchecked Sendable隐藏bug运行时出现数据竞争仅当锁/队列能保证安全时使用
数组/字典的条件一致性集合仅当元素为Sendable时才符合Sendable确保元素类型符合Sendable
Error不符合Sendable"Type does not conform to Sendable"错误使用
any Error & Sendable
或自定义类型错误

Part 3: Task Management

第三部分:任务管理

Task { }

Task { }

Creates an unstructured task that inherits the current actor context and priority.
swift
// Inherits actor context — if called from @MainActor, runs on MainActor
let task = Task {
    try await fetchData()
}

// Get the result
let result = try await task.value

// Get Result<Success, Failure>
let outcome = await task.result
创建一个非结构化任务,继承当前的Actor上下文和优先级。
swift
// 继承Actor上下文——如果从@MainActor调用,则在MainActor上运行
let task = Task {
    try await fetchData()
}

// 获取结果
let result = try await task.value

// 获取Result<Success, Failure>
let outcome = await task.result

Task.detached { }

Task.detached { }

Creates a task with no inherited context. Does not inherit the actor or priority.
swift
Task.detached(priority: .background) {
    // NOT on MainActor even if created from MainActor
    await processLargeFile()
}
When to use: Background work that must NOT run on the calling actor. Prefer
Task {}
in most cases —
Task.detached
is rarely needed.
创建一个不继承任何上下文的任务。不继承Actor或优先级。
swift
Task.detached(priority: .background) {
    // 即使从MainActor创建,也不会在MainActor上运行
    await processLargeFile()
}
使用场景:必须在调用者Actor之外运行的后台工作。大多数情况下优先使用
Task {}
——
Task.detached
很少需要。

Task Cancellation

任务取消

Cancellation is cooperative. Setting cancellation is a request; the task must check and respond.
swift
let task = Task {
    for item in largeCollection {
        // Option 1: Check boolean
        if Task.isCancelled { break }

        // Option 2: Throw CancellationError
        try Task.checkCancellation()

        await process(item)
    }
}

// Request cancellation
task.cancel()
取消是协作式的。设置取消只是一个请求;任务必须自行检查并响应。
swift
let task = Task {
    for item in largeCollection {
        // 方式1:检查布尔值
        if Task.isCancelled { break }

        // 方式2:抛出CancellationError
        try Task.checkCancellation()

        await process(item)
    }
}

// 请求取消
task.cancel()

Task.sleep

Task.sleep

Suspends the current task for a duration. Supports cancellation — throws
CancellationError
if cancelled during sleep.
swift
// Duration-based (preferred)
try await Task.sleep(for: .seconds(2))
try await Task.sleep(for: .milliseconds(500))

// Nanoseconds (older API)
try await Task.sleep(nanoseconds: 2_000_000_000)
暂停当前任务一段时长。支持取消——如果在睡眠期间被取消,会抛出
CancellationError
swift
// 基于时长的调用(推荐)
try await Task.sleep(for: .seconds(2))
try await Task.sleep(for: .milliseconds(500))

// 纳秒级调用(旧API)
try await Task.sleep(nanoseconds: 2_000_000_000)

Task.yield

Task.yield

Voluntarily yields execution to allow other tasks to run. Use in long-running synchronous loops.
swift
for i in 0..<1_000_000 {
    if i.isMultiple(of: 1000) {
        await Task.yield()
    }
    process(i)
}
主动让出执行权,允许其他任务运行。在长时间运行的同步循环中使用。
swift
for i in 0..<1_000_000 {
    if i.isMultiple(of: 1000) {
        await Task.yield()
    }
    process(i)
}

Task Priority

任务优先级

PriorityUse Case
.userInitiated
Direct user action, visible result
.high
Same as .userInitiated
.medium
Default when not specified
.low
Prefetching, non-urgent work
.utility
Long computation, progress shown
.background
Maintenance, cleanup, not time-sensitive
swift
Task(priority: .userInitiated) {
    await loadVisibleContent()
}

Task(priority: .background) {
    await cleanupTempFiles()
}
优先级使用场景
.userInitiated
用户直接操作,结果可见
.high
与.userInitiated相同
.medium
未指定时的默认优先级
.low
预取、非紧急工作
.utility
长时间计算,显示进度
.background
维护、清理、非时间敏感工作
swift
Task(priority: .userInitiated) {
    await loadVisibleContent()
}

Task(priority: .background) {
    await cleanupTempFiles()
}

@TaskLocal

@TaskLocal

Task-scoped values that propagate to child tasks automatically.
swift
enum RequestContext {
    @TaskLocal static var requestID: String?
    @TaskLocal static var userID: String?
}

// Set values for a scope
RequestContext.$requestID.withValue("req-123") {
    RequestContext.$userID.withValue("user-456") {
        // Both values available here and in child tasks
        Task {
            print(RequestContext.requestID)  // "req-123"
            print(RequestContext.userID)     // "user-456"
        }
    }
}

// Outside scope — values are nil
print(RequestContext.requestID)  // nil
Propagation rules:
@TaskLocal
values propagate to child tasks created with
Task {}
. They do NOT propagate to
Task.detached {}
.
任务作用域内的值,会自动传播到子任务。
swift
enum RequestContext {
    @TaskLocal static var requestID: String?
    @TaskLocal static var userID: String?
}

// 在作用域内设置值
RequestContext.$requestID.withValue("req-123") {
    RequestContext.$userID.withValue("user-456") {
        // 此处及子任务中均可访问这两个值
        Task {
            print(RequestContext.requestID)  // "req-123"
            print(RequestContext.userID)     // "user-456"
        }
    }
}

// 作用域外部——值为nil
print(RequestContext.requestID)  // nil
传播规则
@TaskLocal
值会传播给用
Task {}
创建的子任务。不会传播给
Task.detached {}
创建的任务。

Task Gotcha Table

任务问题排查表

GotchaSymptomFix
Task never cancelledResource leak, work continues after view disappearsStore task, cancel in deinit/onDisappear
Ignoring cancellationTask runs to completion even when cancelledCheck Task.isCancelled in loops, use checkCancellation()
Task.detached loses actor context"Not isolated to MainActor"Use Task {} when you need actor isolation
Capturing self in TaskPotential retain cycleUse [weak self] for long-lived tasks
TaskLocal not propagatedValue is nil in detached taskTaskLocal only propagates to child tasks, not detached
Task priority inversionLow-priority task blocks high-prioritySystem handles most cases; avoid awaiting low-priority from high

问题症状修复方案
任务从未被取消资源泄漏,视图消失后工作仍在继续保存任务引用,在deinit/onDisappear中取消
忽略取消请求即使被取消,任务仍运行到完成在循环中检查Task.isCancelled,使用checkCancellation()
Task.detached丢失Actor上下文"Not isolated to MainActor"错误需要Actor隔离时使用Task {}
在Task中捕获self潜在的循环引用对长生命周期任务使用[weak self]
TaskLocal未传播分离任务中的值为nilTaskLocal仅传播给子任务,不传播给分离任务
任务优先级反转低优先级任务阻塞高优先级任务系统会处理大多数情况;避免在高优先级任务中等待低优先级任务

Part 4: Structured Concurrency

第四部分:结构化并发

async let

async let

Run a fixed number of operations in parallel. All
async let
bindings are implicitly awaited when the scope exits.
swift
async let images = fetchImages()
async let metadata = fetchMetadata()
async let config = loadConfig()

// All three run concurrently, await together
let (imgs, meta, cfg) = try await (images, metadata, config)
Semantics: If one
async let
throws, the others are cancelled. All must complete (or be cancelled) before the enclosing scope exits.
并行运行固定数量的操作。当作用域退出时,所有
async let
绑定会被隐式等待。
swift
async let images = fetchImages()
async let metadata = fetchMetadata()
async let config = loadConfig()

// 三个操作并发运行,一起等待结果
let (imgs, meta, cfg) = try await (images, metadata, config)
语义:如果一个
async let
抛出错误,其他操作会被自动取消。所有操作必须完成(或被取消)后,封闭作用域才会退出。

TaskGroup — Non-Throwing

TaskGroup — 非抛出型

Dynamic number of parallel tasks where none throw.
swift
let results = await withTaskGroup(of: String.self) { group in
    for name in names {
        group.addTask {
            await fetchGreeting(for: name)
        }
    }

    var greetings: [String] = []
    for await greeting in group {
        greetings.append(greeting)
    }
    return greetings
}
动态数量的并行任务,且不会抛出错误。
swift
let results = await withTaskGroup(of: String.self) { group in
    for name in names {
        group.addTask {
            await fetchGreeting(for: name)
        }
    }

    var greetings: [String] = []
    for await greeting in group {
        greetings.append(greeting)
    }
    return greetings
}

TaskGroup — Throwing

TaskGroup — 抛出型

Dynamic number of parallel tasks that can throw.
swift
let images = try await withThrowingTaskGroup(of: (URL, UIImage).self) { group in
    for url in urls {
        group.addTask {
            let image = try await downloadImage(url)
            return (url, image)
        }
    }

    var results: [URL: UIImage] = [:]
    for try await (url, image) in group {
        results[url] = image
    }
    return results
}
动态数量的并行任务,可能会抛出错误。
swift
let images = try await withThrowingTaskGroup(of: (URL, UIImage).self) { group in
    for url in urls {
        group.addTask {
            let image = try await downloadImage(url)
            return (url, image)
        }
    }

    var results: [URL: UIImage] = [:]
    for try await (url, image) in group {
        results[url] = image
    }
    return results
}

withDiscardingTaskGroup (iOS 17+)

withDiscardingTaskGroup (iOS 17+)

For when you need concurrency but don't need to collect results.
swift
try await withThrowingDiscardingTaskGroup { group in
    for connection in connections {
        group.addTask {
            try await connection.monitor()
            // Results are discarded — useful for long-running services
        }
    }
    // Group stays alive until all tasks complete or one throws
}
当你需要并发但不需要收集结果时使用。
swift
try await withThrowingDiscardingTaskGroup { group in
    for connection in connections {
        group.addTask {
            try await connection.monitor()
            // 结果会被丢弃——适用于长时间运行的服务
        }
    }
    // 所有任务完成或其中一个抛出错误前,Group会保持活跃
}

TaskGroup Control

TaskGroup控制

swift
await withTaskGroup(of: Data.self) { group in
    // Add tasks conditionally
    group.addTaskUnlessCancelled {
        await fetchData()
    }

    // Cancel remaining tasks
    group.cancelAll()

    // Wait without collecting
    await group.waitForAll()

    // Iterate one at a time
    while let result = await group.next() {
        process(result)
    }
}
swift
await withTaskGroup(of: Data.self) { group in
    // 有条件地添加任务
    group.addTaskUnlessCancelled {
        await fetchData()
    }

    // 取消剩余任务
    group.cancelAll()

    // 等待所有任务完成但不收集结果
    await group.waitForAll()

    // 逐个获取结果
    while let result = await group.next() {
        process(result)
    }
}

Task Tree Semantics

任务树语义

Structured concurrency forms a tree:
  • Parent cancellation cancels all children — cancelling a task cancels all
    async let
    and TaskGroup children
  • Child error propagates to parent — in throwing groups, a child error cancels siblings and propagates up
  • All children must complete before parent returns — the scope awaits all children, even cancelled ones
swift
// If fetchImages() throws, fetchMetadata() is automatically cancelled
async let images = fetchImages()
async let metadata = fetchMetadata()
let result = try await (images, metadata)
结构化并发形成一个树状结构:
  • 父任务取消会取消所有子任务——取消一个任务会取消所有
    async let
    和TaskGroup子任务
  • 子任务错误会传播到父任务——在抛出型Group中,子任务错误会取消兄弟任务并向上传播
  • 所有子任务必须在父任务返回前完成——作用域会等待所有子任务,即使是已取消的任务
swift
// 如果fetchImages()抛出错误,fetchMetadata()会被自动取消
async let images = fetchImages()
async let metadata = fetchMetadata()
let result = try await (images, metadata)

Structured Concurrency Gotcha Table

结构化并发问题排查表

GotchaSymptomFix
async let unusedWork still executes but result is discarded silentlyAssign all async let results or use withDiscardingTaskGroup
TaskGroup accumulating memoryMemory grows with 10K+ tasksProcess results as they arrive, don't collect all
Capturing mutable state in addTask"Mutation of captured var"Use let binding or actor
Not handling partial failureSome tasks succeed, some failUse group.next() and handle errors individually
async let in loopCompiler error — async let must be in fixed positionsUse TaskGroup instead
Returning from group earlyRemaining tasks still runCall group.cancelAll() before returning

问题症状修复方案
async let未使用工作仍会执行,但结果被静默丢弃接收所有async let的结果,或使用withDiscardingTaskGroup
TaskGroup内存累积任务数量超过10K时内存增长实时处理结果,不要全部收集
在addTask中捕获可变状态"Mutation of captured var"错误使用let绑定或Actor
未处理部分失败部分任务成功,部分失败使用group.next()并单独处理错误
在循环中使用async let编译器错误——async let必须在固定位置使用改用TaskGroup
提前从Group返回剩余任务仍在运行返回前调用group.cancelAll()

Part 5: Async Sequences

第五部分:异步序列

AsyncStream

AsyncStream

Non-throwing stream for producing values over time.
swift
let stream = AsyncStream<Int> { continuation in
    for i in 0..<10 {
        continuation.yield(i)
    }
    continuation.finish()
}

for await value in stream {
    print(value)
}
用于随时间生成值的非抛出型流。
swift
let stream = AsyncStream<Int> { continuation in
    for i in 0..<10 {
        continuation.yield(i)
    }
    continuation.finish()
}

for await value in stream {
    print(value)
}

AsyncThrowingStream

AsyncThrowingStream

Stream that can fail with an error.
swift
let stream = AsyncThrowingStream<Data, Error> { continuation in
    let monitor = NetworkMonitor()
    monitor.onData = { data in
        continuation.yield(data)
    }
    monitor.onError = { error in
        continuation.finish(throwing: error)
    }
    monitor.onComplete = {
        continuation.finish()
    }

    continuation.onTermination = { @Sendable _ in
        monitor.stop()
    }

    monitor.start()
}

do {
    for try await data in stream {
        process(data)
    }
} catch {
    handleStreamError(error)
}
可能会抛出错误的流。
swift
let stream = AsyncThrowingStream<Data, Error> { continuation in
    let monitor = NetworkMonitor()
    monitor.onData = { data in
        continuation.yield(data)
    }
    monitor.onError = { error in
        continuation.finish(throwing: error)
    }
    monitor.onComplete = {
        continuation.finish()
    }

    continuation.onTermination = { @Sendable _ in
        monitor.stop()
    }

    monitor.start()
}

do {
    for try await data in stream {
        process(data)
    }
} catch {
    handleStreamError(error)
}

Continuation API

Continuation API

swift
let stream = AsyncStream<Value> { continuation in
    // Emit a value
    continuation.yield(value)

    // End the stream normally
    continuation.finish()

    // Cleanup when consumer cancels or stream ends
    continuation.onTermination = { @Sendable termination in
        switch termination {
        case .cancelled:
            cleanup()
        case .finished:
            finalCleanup()
        @unknown default:
            break
        }
    }
}

// For throwing streams
let stream = AsyncThrowingStream<Value, Error> { continuation in
    continuation.yield(value)
    continuation.finish()                  // Normal end
    continuation.finish(throwing: error)   // End with error
}
swift
let stream = AsyncStream<Value> { continuation in
    // 发送一个值
    continuation.yield(value)

    // 正常结束流
    continuation.finish()

    // 当消费者取消或流结束时清理资源
    continuation.onTermination = { @Sendable termination in
        switch termination {
        case .cancelled:
            cleanup()
        case .finished:
            finalCleanup()
        @unknown default:
            break
        }
    }
}

// 对于抛出型流
let stream = AsyncThrowingStream<Value, Error> { continuation in
    continuation.yield(value)
    continuation.finish()                  // 正常结束
    continuation.finish(throwing: error)   // 带错误结束
}

Buffering Policies

缓冲策略

Control what happens when values are produced faster than consumed.
swift
// Keep all values (default) — memory can grow unbounded
let stream = AsyncStream<Int>(bufferingPolicy: .unbounded) { continuation in
    // ...
}

// Keep oldest N values, drop new ones when buffer is full
let stream = AsyncStream<Int>(bufferingPolicy: .bufferingOldest(100)) { continuation in
    // ...
}

// Keep newest N values, drop old ones when buffer is full
let stream = AsyncStream<Int>(bufferingPolicy: .bufferingNewest(100)) { continuation in
    // ...
}
PolicyBehaviorUse When
.unbounded
Keeps all valuesConsumer keeps up, or bounded producer
.bufferingOldest(N)
Drops new values when fullOrder matters, older values have priority
.bufferingNewest(N)
Drops old values when fullLatest state matters (UI updates, sensor data)
控制当值生成速度快于消费速度时的行为。
swift
// 保留所有值(默认)——内存可能无限制增长
let stream = AsyncStream<Int>(bufferingPolicy: .unbounded) { continuation in
    // ...
}

// 保留最早的N个值,缓冲区满时丢弃新值
let stream = AsyncStream<Int>(bufferingPolicy: .bufferingOldest(100)) { continuation in
    // ...
}

// 保留最新的N个值,缓冲区满时丢弃旧值
let stream = AsyncStream<Int>(bufferingPolicy: .bufferingNewest(100)) { continuation in
    // ...
}
策略行为使用场景
.unbounded
保留所有值消费者能跟上生产速度,或生产者有界
.bufferingOldest(N)
缓冲区满时丢弃新值顺序重要,旧值优先级更高
.bufferingNewest(N)
缓冲区满时丢弃旧值最新状态重要(UI更新、传感器数据)

Custom AsyncSequence

自定义AsyncSequence

swift
struct Counter: AsyncSequence {
    typealias Element = Int
    let limit: Int

    struct AsyncIterator: AsyncIteratorProtocol {
        var current = 0
        let limit: Int

        mutating func next() async -> Int? {
            guard current < limit else { return nil }
            defer { current += 1 }
            return current
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(limit: limit)
    }
}

// Usage
for await number in Counter(limit: 5) {
    print(number)  // 0, 1, 2, 3, 4
}
swift
struct Counter: AsyncSequence {
    typealias Element = Int
    let limit: Int

    struct AsyncIterator: AsyncIteratorProtocol {
        var current = 0
        let limit: Int

        mutating func next() async -> Int? {
            guard current < limit else { return nil }
            defer { current += 1 }
            return current
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(limit: limit)
    }
}

// 使用方式
for await number in Counter(limit: 5) {
    print(number)  // 0, 1, 2, 3, 4
}

AsyncSequence Operators

AsyncSequence操作符

Standard operators work on any
AsyncSequence
:
swift
// Map
for await name in users.map(\.name) { }

// Filter
for await adult in users.filter({ $0.age >= 18 }) { }

// CompactMap
for await image in urls.compactMap({ await tryLoadImage($0) }) { }

// Prefix
for await first5 in stream.prefix(5) { }

// first(where:)
let match = await stream.first(where: { $0 > threshold })

// Contains
let hasMatch = await stream.contains(where: { $0 > threshold })

// Reduce
let sum = await numbers.reduce(0, +)
标准操作符可用于任何
AsyncSequence
swift
// Map
for await name in users.map(\.name) { }

// Filter
for await adult in users.filter({ $0.age >= 18 }) { }

// CompactMap
for await image in urls.compactMap({ await tryLoadImage($0) }) { }

// Prefix
for await first5 in stream.prefix(5) { }

// first(where:)
let match = await stream.first(where: { $0 > threshold })

// Contains
let hasMatch = await stream.contains(where: { $0 > threshold })

// Reduce
let sum = await numbers.reduce(0, +)

Built-in Async Sequences

内置AsyncSequence

swift
// NotificationCenter
for await notification in NotificationCenter.default.notifications(named: .didUpdate) {
    handleUpdate(notification)
}

// URLSession bytes
let (bytes, response) = try await URLSession.shared.bytes(from: url)
for try await byte in bytes {
    process(byte)
}

// FileHandle bytes
for try await line in FileHandle.standardInput.bytes.lines {
    process(line)
}
swift
// NotificationCenter
for await notification in NotificationCenter.default.notifications(named: .didUpdate) {
    handleUpdate(notification)
}

// URLSession bytes
let (bytes, response) = try await URLSession.shared.bytes(from: url)
for try await byte in bytes {
    process(byte)
}

// FileHandle bytes
for try await line in FileHandle.standardInput.bytes.lines {
    process(line)
}

Async Sequence Gotcha Table

异步序列问题排查表

GotchaSymptomFix
Continuation yielded after finishRuntime warning, value lostTrack finished state, guard before yield
Stream never finishingfor-await loop hangs foreverAlways call continuation.finish() in all code paths
No onTermination handlerResource leak when consumer cancelsSet continuation.onTermination for cleanup
Unbounded bufferMemory growth under loadUse .bufferingNewest(N) or .bufferingOldest(N)
Multiple consumersOnly first consumer gets valuesAsyncStream is single-consumer; create separate streams per consumer
for-await on MainActorUI freezes waiting for valuesUse Task {} to consume off the main path

问题症状修复方案
finish后调用yield运行时警告,值丢失跟踪完成状态,调用yield前检查
流从未结束for-await循环永远挂起在所有代码路径中调用continuation.finish()
无onTermination处理程序消费者取消时资源泄漏设置continuation.onTermination进行清理
无界缓冲区负载下内存增长使用.bufferingNewest(N)或.bufferingOldest(N)
多个消费者只有第一个消费者能获取值AsyncStream是单消费者的;为每个消费者创建单独的流
在MainActor上使用for-awaitUI冻结等待值使用Task {}在主线程之外消费

Part 6: Isolation Patterns

第六部分:隔离模式

@MainActor on Functions

函数上的@MainActor

swift
@MainActor
func updateUI() {
    label.text = "Done"
}

// Call from async context
func doWork() async {
    let result = await computeResult()
    await updateUI()           // Hops to MainActor
}
swift
@MainActor
func updateUI() {
    label.text = "Done"
}

// 从异步上下文调用
func doWork() async {
    let result = await computeResult()
    await updateUI()           // 切换到MainActor
}

MainActor.run

MainActor.run

Explicitly execute a closure on the main actor from any context.
swift
func processData() async {
    let result = await heavyComputation()

    await MainActor.run {
        self.label.text = result
        self.progressView.isHidden = true
    }
}
从任何上下文显式在主Actor上执行闭包。
swift
func processData() async {
    let result = await heavyComputation()

    await MainActor.run {
        self.label.text = result
        self.progressView.isHidden = true
    }
}

MainActor.assumeIsolated (iOS 17+)

MainActor.assumeIsolated (iOS 17+)

Assert that code is already running on the main actor. Crashes at runtime if the assertion is false.
swift
func legacyCallback() {
    // We KNOW this is called on main thread (UIKit guarantee)
    MainActor.assumeIsolated {
        self.viewModel.update()    // Access @MainActor state
    }
}
See
axiom-assume-isolated
for comprehensive patterns.
断言代码已在主Actor上运行。如果断言为假,运行时会崩溃。
swift
func legacyCallback() {
    // 我们确定此方法在主线程上调用(UIKit保证)
    MainActor.assumeIsolated {
        self.viewModel.update()    // 访问@MainActor状态
    }
}
有关完整模式,请参考
axiom-assume-isolated

nonisolated

nonisolated

Opt out of the enclosing actor's isolation.
swift
@MainActor
class ViewModel {
    let id: UUID                           // Implicitly nonisolated (let)

    nonisolated var analyticsID: String {   // Explicitly nonisolated
        id.uuidString
    }

    var items: [Item] = []                 // Isolated to MainActor
}
退出封闭Actor的隔离。
swift
@MainActor
class ViewModel {
    let id: UUID                           // 隐式非隔离(let)

    nonisolated var analyticsID: String {   // 显式非隔离
        id.uuidString
    }

    var items: [Item] = []                 // 隔离到MainActor
}

nonisolated(unsafe)

nonisolated(unsafe)

Compiler escape hatch. Tells the compiler to treat a property as if it's not isolated, without any safety guarantees.
swift
// Use only when you have external guarantees of thread safety
nonisolated(unsafe) var legacyState: Int = 0

// Common for global constants that the compiler can't verify
nonisolated(unsafe) let formatter: DateFormatter = {
    let f = DateFormatter()
    f.dateStyle = .medium
    return f
}()
Warning:
nonisolated(unsafe)
provides zero runtime protection. Data races will not be caught. Use only as a last resort for bridging legacy code.
编译器的逃生舱口。告诉编译器将属性视为非隔离,但不提供任何安全保证。
swift
// 仅当你有外部线程安全保证时使用
nonisolated(unsafe) var legacyState: Int = 0

// 常用于编译器无法验证的全局常量
nonisolated(unsafe) let formatter: DateFormatter = {
    let f = DateFormatter()
    f.dateStyle = .medium
    return f
}()
警告
nonisolated(unsafe)
不提供任何运行时保护。数据竞争不会被捕获。仅作为桥接遗留代码的最后手段使用。

@preconcurrency

@preconcurrency

Suppress concurrency warnings for pre-concurrency APIs during migration.
swift
// Suppress warnings for entire module
@preconcurrency import MyLegacyFramework

// Suppress for specific protocol conformance
class MyDelegate: @preconcurrency SomeLegacyDelegate {
    func delegateCallback() {
        // No Sendable warnings for this conformance
    }
}
在迁移过程中,抑制针对预并发API的并发警告。
swift
// 抑制整个模块的警告
@preconcurrency import MyLegacyFramework

// 抑制特定协议一致性的警告
class MyDelegate: @preconcurrency SomeLegacyDelegate {
    func delegateCallback() {
        // 此一致性不会有Sendable警告
    }
}

#isolation (Swift 5.9+)

#isolation (Swift 5.9+)

Capture the caller's isolation context so a function runs on whatever actor the caller is on.
swift
func doWork(isolation: isolated (any Actor)? = #isolation) async {
    // Runs on caller's actor — no hop if caller is already isolated
    performWork()
}

// Called from @MainActor — runs on MainActor
@MainActor
func setup() async {
    await doWork()             // doWork runs on MainActor
}

// Called from custom actor — runs on that actor
actor MyActor {
    func run() async {
        await doWork()         // doWork runs on MyActor
    }
}
捕获调用者的隔离上下文,使函数在调用者所在的任何Actor上运行。
swift
func doWork(isolation: isolated (any Actor)? = #isolation) async {
    // 在调用者的Actor上运行——如果调用者已隔离,则无需切换
    performWork()
}

// 从@MainActor调用——在MainActor上运行
@MainActor
func setup() async {
    await doWork()             // doWork在MainActor上运行
}

// 从自定义Actor调用——在该Actor上运行
actor MyActor {
    func run() async {
        await doWork()         // doWork在MyActor上运行
    }
}

Isolation Gotcha Table

隔离问题排查表

GotchaSymptomFix
MainActor.run from MainActorUnnecessary hop, potential deadlock riskCheck context or use assumeIsolated
nonisolated(unsafe) data raceCrash at runtime, corrupted stateUse proper isolation or Mutex
@preconcurrency hiding real issuesRuntime crashes in productionMigrate to proper concurrency before shipping
#isolation not available pre-5.9Compiler errorUse traditional @MainActor annotation
nonisolated on actor methodCan't access any isolated stateOnly use for computed properties from non-isolated state

问题症状修复方案
从MainActor调用MainActor.run不必要的上下文切换,潜在死锁风险检查上下文或使用assumeIsolated
nonisolated(unsafe)导致数据竞争运行时崩溃,状态损坏使用正确的隔离或Mutex
@preconcurrency隐藏真实问题生产环境中运行时崩溃上线前迁移到正确的并发模式
Swift 5.9之前不支持#isolation编译器错误使用传统的@MainActor注解
Actor方法上的nonisolated无法访问任何隔离状态仅用于从非隔离状态计算的属性

Part 7: Continuations

第七部分:Continuation

Bridge callback-based APIs to async/await.
将基于回调的API桥接到async/await。

withCheckedContinuation

withCheckedContinuation

Non-throwing bridge.
swift
func currentLocation() async -> CLLocation {
    await withCheckedContinuation { continuation in
        locationManager.requestLocation { location in
            continuation.resume(returning: location)
        }
    }
}
非抛出型桥接。
swift
func currentLocation() async -> CLLocation {
    await withCheckedContinuation { continuation in
        locationManager.requestLocation { location in
            continuation.resume(returning: location)
        }
    }
}

withCheckedThrowingContinuation

withCheckedThrowingContinuation

Throwing bridge.
swift
func fetchUser(id: String) async throws -> User {
    try await withCheckedThrowingContinuation { continuation in
        api.fetchUser(id: id) { result in
            switch result {
            case .success(let user):
                continuation.resume(returning: user)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
}
抛出型桥接。
swift
func fetchUser(id: String) async throws -> User {
    try await withCheckedThrowingContinuation { continuation in
        api.fetchUser(id: id) { result in
            switch result {
            case .success(let user):
                continuation.resume(returning: user)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
}

Continuation Resume Methods

Continuation恢复方法

swift
// Return a value
continuation.resume(returning: value)

// Throw an error
continuation.resume(throwing: error)

// From a Result type
continuation.resume(with: result)    // Result<T, Error>
swift
// 返回一个值
continuation.resume(returning: value)

// 抛出一个错误
continuation.resume(throwing: error)

// 从Result类型恢复
continuation.resume(with: result)    // Result<T, Error>

Resume-Exactly-Once Rule

恰好恢复一次规则

A continuation MUST be resumed exactly once:
  • Resuming twice crashes with
    "Continuation already resumed"
    (checked) or undefined behavior (unsafe)
  • Never resuming causes the awaiting task to hang forever — a silent leak
swift
// DANGEROUS: callback might not be called
func riskyBridge() async throws -> Data {
    try await withCheckedThrowingContinuation { continuation in
        api.fetch { data, error in
            if let error {
                continuation.resume(throwing: error)
                return
            }
            if let data {
                continuation.resume(returning: data)
                return
            }
            // BUG: if both are nil, continuation is never resumed
            // Fix: add a fallback
            continuation.resume(throwing: BridgeError.noResponse)
        }
    }
}
一个Continuation必须恰好被恢复一次
  • 恢复两次会导致
    "Continuation already resumed"
    崩溃(checked类型)或未定义行为(unsafe类型)
  • 从未恢复会导致等待的任务永远挂起——静默泄漏
swift
// 危险:回调可能不会被调用
func riskyBridge() async throws -> Data {
    try await withCheckedThrowingContinuation { continuation in
        api.fetch { data, error in
            if let error {
                continuation.resume(throwing: error)
                return
            }
            if let data {
                continuation.resume(returning: data)
                return
            }
            // 错误:如果两者都为nil,Continuation永远不会被恢复
            // 修复:添加回退
            continuation.resume(throwing: BridgeError.noResponse)
        }
    }
}

Bridging Delegates

桥接Delegate

swift
class LocationBridge: NSObject, CLLocationManagerDelegate {
    private var continuation: CheckedContinuation<CLLocation, Error>?
    private let manager = CLLocationManager()

    func requestLocation() async throws -> CLLocation {
        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
            manager.delegate = self
            manager.requestLocation()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        continuation?.resume(returning: locations[0])
        continuation = nil     // Prevent double resume
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        continuation?.resume(throwing: error)
        continuation = nil
    }
}
swift
class LocationBridge: NSObject, CLLocationManagerDelegate {
    private var continuation: CheckedContinuation<CLLocation, Error>?
    private let manager = CLLocationManager()

    func requestLocation() async throws -> CLLocation {
        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
            manager.delegate = self
            manager.requestLocation()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        continuation?.resume(returning: locations[0])
        continuation = nil     // 防止重复恢复
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        continuation?.resume(throwing: error)
        continuation = nil
    }
}

Unsafe Continuations

Unsafe Continuation

Skip runtime checks for performance. Same API as checked, but misuse causes undefined behavior instead of a diagnostic crash.
swift
func fastBridge() async -> Data {
    await withUnsafeContinuation { continuation in
        // No runtime check for double-resume or missing resume
        fastCallback { data in
            continuation.resume(returning: data)
        }
    }
}
Use checked continuations during development, switch to unsafe only after thorough testing and when profiling shows the check is a bottleneck.
跳过运行时检查以提升性能。API与checked类型相同,但误用会导致未定义行为而非诊断性崩溃。
swift
func fastBridge() async -> Data {
    await withUnsafeContinuation { continuation in
        // 没有运行时检查来防止重复恢复或未恢复
        fastCallback { data in
            continuation.resume(returning: data)
        }
    }
}
开发期间使用checked continuation,仅在经过全面测试且性能分析显示检查是瓶颈时,才切换到unsafe类型。

Continuation Gotcha Table

Continuation问题排查表

GotchaSymptomFix
Resume called twice"Continuation already resumed" crashSet continuation to nil after resume
Resume never calledTask hangs indefinitelyEnsure all code paths resume — including error/nil cases
Capturing continuationContinuation escapes scopeStore in property, ensure single resume
Unsafe continuation in debugNo diagnostics for misuseUse withCheckedContinuation during development
Delegate called multiple timesCrash on second resumeUse AsyncStream instead of continuation for repeated callbacks
Callback on wrong threadDoesn't matter for continuationContinuations can be resumed from any thread

问题症状修复方案
恢复被调用两次"Continuation already resumed"崩溃恢复后将continuation设为nil
从未恢复任务无限期挂起确保所有代码路径都恢复——包括错误/空值情况
捕获continuationContinuation逃逸出作用域存储在属性中,确保仅恢复一次
调试时使用Unsafe continuation误用无诊断信息开发期间使用withCheckedContinuation
Delegate被多次调用第二次恢复时崩溃对于重复回调,改用AsyncStream而非continuation
回调在错误线程上调用对continuation无影响Continuation可从任何线程恢复

Part 8: Migration Patterns

第八部分:迁移模式

Common migrations from GCD and completion handlers to Swift concurrency.
从GCD和完成处理程序迁移到Swift并发的常见模式。

DispatchQueue to Actor

DispatchQueue到Actor

swift
// BEFORE: DispatchQueue for thread safety
class ImageCache {
    private let queue = DispatchQueue(label: "cache", attributes: .concurrent)
    private var cache: [URL: UIImage] = [:]

    func get(_ url: URL, completion: @escaping (UIImage?) -> Void) {
        queue.async { completion(self.cache[url]) }
    }

    func set(_ url: URL, image: UIImage) {
        queue.async(flags: .barrier) { self.cache[url] = image }
    }
}

// AFTER: Actor
actor ImageCache {
    private var cache: [URL: UIImage] = [:]

    func get(_ url: URL) -> UIImage? {
        cache[url]
    }

    func set(_ url: URL, image: UIImage) {
        cache[url] = image
    }
}
swift
// 之前:使用DispatchQueue保证线程安全
class ImageCache {
    private let queue = DispatchQueue(label: "cache", attributes: .concurrent)
    private var cache: [URL: UIImage] = [:]

    func get(_ url: URL, completion: @escaping (UIImage?) -> Void) {
        queue.async { completion(self.cache[url]) }
    }

    func set(_ url: URL, image: UIImage) {
        queue.async(flags: .barrier) { self.cache[url] = image }
    }
}

// 之后:使用Actor
actor ImageCache {
    private var cache: [URL: UIImage] = [:]

    func get(_ url: URL) -> UIImage? {
        cache[url]
    }

    func set(_ url: URL, image: UIImage) {
        cache[url] = image
    }
}

DispatchGroup to TaskGroup

DispatchGroup到TaskGroup

swift
// BEFORE: DispatchGroup
let group = DispatchGroup()
var results: [Data] = []
for url in urls {
    group.enter()
    fetch(url) { data in
        results.append(data)
        group.leave()
    }
}
group.notify(queue: .main) { use(results) }

// AFTER: TaskGroup
let results = await withTaskGroup(of: Data.self) { group in
    for url in urls {
        group.addTask { await fetch(url) }
    }
    var collected: [Data] = []
    for await data in group {
        collected.append(data)
    }
    return collected
}
use(results)
swift
// 之前:使用DispatchGroup
let group = DispatchGroup()
var results: [Data] = []
for url in urls {
    group.enter()
    fetch(url) { data in
        results.append(data)
        group.leave()
    }
}
group.notify(queue: .main) { use(results) }

// 之后:使用TaskGroup
let results = await withTaskGroup(of: Data.self) { group in
    for url in urls {
        group.addTask { await fetch(url) }
    }
    var collected: [Data] = []
    for await data in group {
        collected.append(data)
    }
    return collected
}
use(results)

Completion Handler to async

完成处理程序到async

swift
// BEFORE
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, error in
        if let error { completion(.failure(error)); return }
        guard let data else { completion(.failure(FetchError.noData)); return }
        completion(.success(data))
    }.resume()
}

// AFTER
func fetchData() async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}
swift
// 之前
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, error in
        if let error { completion(.failure(error)); return }
        guard let data else { completion(.failure(FetchError.noData)); return }
        completion(.success(data))
    }.resume()
}

// 之后
func fetchData() async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

@objc Delegates with @MainActor

带@MainActor的@objc Delegate

swift
@MainActor
class ViewController: UIViewController, UITableViewDelegate {
    // @objc delegate methods inherit @MainActor isolation from the class
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Already on MainActor — safe to update UI
        updateSelection(indexPath)
    }
}
swift
@MainActor
class ViewController: UIViewController, UITableViewDelegate {
    // @objc delegate方法从类继承@MainActor隔离
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 已在MainActor上——可安全更新UI
        updateSelection(indexPath)
    }
}

NotificationCenter to AsyncSequence

NotificationCenter到AsyncSequence

swift
// BEFORE
let observer = NotificationCenter.default.addObserver(
    forName: .didUpdate, object: nil, queue: .main
) { notification in
    handleUpdate(notification)
}
// Must remove observer in deinit

// AFTER
let task = Task {
    for await notification in NotificationCenter.default.notifications(named: .didUpdate) {
        await handleUpdate(notification)
    }
}
// Cancel task in deinit — no manual observer removal needed
swift
// 之前
let observer = NotificationCenter.default.addObserver(
    forName: .didUpdate, object: nil, queue: .main
) { notification in
    handleUpdate(notification)
}
// 必须在deinit中移除观察者

// 之后
let task = Task {
    for await notification in NotificationCenter.default.notifications(named: .didUpdate) {
        await handleUpdate(notification)
    }
}
// 在deinit中取消任务——无需手动移除观察者

Timer to AsyncSequence

Timer到AsyncSequence

swift
// BEFORE
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    updateUI()
}
// Must invalidate in deinit

// AFTER
let task = Task {
    while !Task.isCancelled {
        await updateUI()
        try? await Task.sleep(for: .seconds(1))
    }
}
// Cancel task in deinit
swift
// 之前
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    updateUI()
}
// 必须在deinit中失效

// 之后
let task = Task {
    while !Task.isCancelled {
        await updateUI()
        try? await Task.sleep(for: .seconds(1))
    }
}
// 在deinit中取消任务

DispatchSemaphore to Actor

DispatchSemaphore到Actor

swift
// BEFORE: Semaphore to limit concurrent operations
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
    DispatchQueue.global().async {
        semaphore.wait()
        defer { semaphore.signal() }
        download(url)
    }
}

// AFTER: TaskGroup with limited concurrency
await withTaskGroup(of: Void.self) { group in
    var inFlight = 0
    for url in urls {
        if inFlight >= 3 {
            await group.next()   // Wait for one to finish
            inFlight -= 1
        }
        group.addTask { await download(url) }
        inFlight += 1
    }
    await group.waitForAll()
}
swift
// 之前:使用Semaphore限制并发操作数量
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
    DispatchQueue.global().async {
        semaphore.wait()
        defer { semaphore.signal() }
        download(url)
    }
}

// 之后:使用带有限制并发的TaskGroup
await withTaskGroup(of: Void.self) { group in
    var inFlight = 0
    for url in urls {
        if inFlight >= 3 {
            await group.next()   // 等待一个任务完成
            inFlight -= 1
        }
        group.addTask { await download(url) }
        inFlight += 1
    }
    await group.waitForAll()
}

Migration Gotcha Table

迁移问题排查表

GotchaSymptomFix
DispatchQueue.sync to actorDeadlock potentialRemove .sync, use await
Global dispatch to actor contentionSlowdown from serializationProfile with Concurrency Instruments
Legacy delegate + Sendable"Cannot conform to Sendable"Use @preconcurrency import or @MainActor isolation
Callback called multiple timesContinuation crashUse AsyncStream instead of continuation
Semaphore.wait in async contextThread starvation, potential deadlockUse TaskGroup with manual concurrency limiting
DispatchQueue.main.async to MainActorSubtle timing differencesMainActor.run is the equivalent — test edge cases

问题症状修复方案
DispatchQueue.sync到Actor潜在死锁移除.sync,使用await
全局调度到Actor导致竞争序列化导致性能下降使用并发工具进行性能分析
遗留Delegate + Sendable"Cannot conform to Sendable"错误使用@preconcurrency import或@MainActor隔离
回调被多次调用Continuation崩溃改用AsyncStream而非continuation
异步上下文中的DispatchSemaphore.wait线程饥饿,潜在死锁使用带手动并发限制的TaskGroup
DispatchQueue.main.async到MainActor细微的时序差异MainActor.run是等效方案——测试边缘情况

API Quick Reference

API快速参考

TaskAPISwift Version
Define isolated type
actor MyActor { }
5.5+
Run on main thread
@MainActor
5.5+
Mark as safe to share
: Sendable
5.5+
Mark closure safe to share
@Sendable
5.5+
Parallel tasks (fixed)
async let
5.5+
Parallel tasks (dynamic)
withTaskGroup
5.5+
Stream values
AsyncStream
5.5+
Bridge callback
withCheckedContinuation
5.5+
Check cancellation
Task.checkCancellation()
5.5+
Task-scoped values
@TaskLocal
5.5+
Assert isolation
MainActor.assumeIsolated
5.9+ (iOS 17+)
Capture caller isolation
#isolation
5.9+
Lock-based sync
Mutex
6.0+ (iOS 18+)
Discard results
withDiscardingTaskGroup
5.9+ (iOS 17+)
Transfer ownership
sending
parameter
6.0+
Force background
@concurrent
6.2+
Isolated conformance
extension: @MainActor Proto
6.2+
任务APISwift版本
定义隔离类型
actor MyActor { }
5.5+
在主线程运行
@MainActor
5.5+
标记为可安全共享
: Sendable
5.5+
标记闭包可安全共享
@Sendable
5.5+
并行任务(固定数量)
async let
5.5+
并行任务(动态数量)
withTaskGroup
5.5+
生成值流
AsyncStream
5.5+
桥接回调
withCheckedContinuation
5.5+
检查取消
Task.checkCancellation()
5.5+
任务作用域值
@TaskLocal
5.5+
断言隔离
MainActor.assumeIsolated
5.9+ (iOS 17+)
捕获调用者隔离
#isolation
5.9+
基于锁的同步
Mutex
6.0+ (iOS 18+)
丢弃结果
withDiscardingTaskGroup
5.9+ (iOS 17+)
转移所有权
sending
参数
6.0+
强制后台运行
@concurrent
6.2+
隔离式一致性
extension: @MainActor Proto
6.2+

Resources

资源

WWDC: 2021-10132, 2021-10134, 2022-110350, 2025-268
Docs: /swift/concurrency, /swift/actor, /swift/sendable, /swift/taskgroup
Skills: swift-concurrency, assume-isolated, synchronization, concurrency-profiling
WWDC:2021-10132, 2021-10134, 2022-110350, 2025-268
文档:/swift/concurrency, /swift/actor, /swift/sendable, /swift/taskgroup
技能:swift-concurrency, assume-isolated, synchronization, concurrency-profiling