swift-concurrency-6-2

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Swift 6.2 Approachable Concurrency

Swift 6.2 易用并发模型

Patterns for adopting Swift 6.2's concurrency model where code runs single-threaded by default and concurrency is introduced explicitly. Eliminates common data-race errors without sacrificing performance.
本文介绍如何采用Swift 6.2的并发模型:代码默认以单线程运行,并发需显式声明。该模型可在不牺牲性能的前提下,消除常见的数据竞争错误。

When to Activate

启用场景

  • Migrating Swift 5.x or 6.0/6.1 projects to Swift 6.2
  • Resolving data-race safety compiler errors
  • Designing MainActor-based app architecture
  • Offloading CPU-intensive work to background threads
  • Implementing protocol conformances on MainActor-isolated types
  • Enabling Approachable Concurrency build settings in Xcode 26
  • 将Swift 5.x或6.0/6.1项目迁移至Swift 6.2
  • 解决数据竞争安全相关的编译器错误
  • 设计基于MainActor的应用架构
  • 将CPU密集型任务卸载至后台线程
  • 在MainActor隔离的类型上实现协议
  • 在Xcode 26中启用“易用并发”构建设置

Core Problem: Implicit Background Offloading

核心问题:隐式后台任务卸载

In Swift 6.1 and earlier, async functions could be implicitly offloaded to background threads, causing data-race errors even in seemingly safe code:
swift
// Swift 6.1: ERROR
@MainActor
final class StickerModel {
    let photoProcessor = PhotoProcessor()

    func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
        guard let data = try await item.loadTransferable(type: Data.self) else { return nil }

        // Error: Sending 'self.photoProcessor' risks causing data races
        return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
    }
}
Swift 6.2 fixes this: async functions stay on the calling actor by default.
swift
// Swift 6.2: OK — async stays on MainActor, no data race
@MainActor
final class StickerModel {
    let photoProcessor = PhotoProcessor()

    func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
        guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
        return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
    }
}
在Swift 6.1及更早版本中,异步函数可能会被隐式卸载到后台线程,即使在看似安全的代码中也会引发数据竞争错误:
swift
// Swift 6.1: 错误
@MainActor
final class StickerModel {
    let photoProcessor = PhotoProcessor()

    func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
        guard let data = try await item.loadTransferable(type: Data.self) else { return nil }

        // 错误:传递'self.photoProcessor'可能导致数据竞争
        return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
    }
}
Swift 6.2修复了该问题:异步函数默认保持在调用方的Actor线程上执行。
swift
// Swift 6.2: 正常——异步函数在MainActor线程执行,无数据竞争
@MainActor
final class StickerModel {
    let photoProcessor = PhotoProcessor()

    func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
        guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
        return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
    }
}

Core Pattern — Isolated Conformances

核心模式:隔离协议实现

MainActor types can now conform to non-isolated protocols safely:
swift
protocol Exportable {
    func export()
}

// Swift 6.1: ERROR — crosses into main actor-isolated code
// Swift 6.2: OK with isolated conformance
extension StickerModel: @MainActor Exportable {
    func export() {
        photoProcessor.exportAsPNG()
    }
}
The compiler ensures the conformance is only used on the main actor:
swift
// OK — ImageExporter is also @MainActor
@MainActor
struct ImageExporter {
    var items: [any Exportable]

    mutating func add(_ item: StickerModel) {
        items.append(item)  // Safe: same actor isolation
    }
}

// ERROR — nonisolated context can't use MainActor conformance
nonisolated struct ImageExporter {
    var items: [any Exportable]

    mutating func add(_ item: StickerModel) {
        items.append(item)  // Error: Main actor-isolated conformance cannot be used here
    }
}
MainActor类型现在可以安全地实现非隔离协议:
swift
protocol Exportable {
    func export()
}

// Swift 6.1: 错误——跨域访问MainActor隔离的代码
// Swift 6.2: 通过隔离协议实现可正常运行
extension StickerModel: @MainActor Exportable {
    func export() {
        photoProcessor.exportAsPNG()
    }
}
编译器会确保该协议实现仅在MainActor线程上使用:
swift
// 正常——ImageExporter同样标记为@MainActor
@MainActor
struct ImageExporter {
    var items: [any Exportable]

    mutating func add(_ item: StickerModel) {
        items.append(item)  // 安全:同一Actor隔离域
    }
}

// 错误——非隔离上下文无法使用MainActor隔离的协议实现
nonisolated struct ImageExporter {
    var items: [any Exportable]

    mutating func add(_ item: StickerModel) {
        items.append(item)  // 错误:此处无法使用MainActor隔离的协议实现
    }
}

Core Pattern — Global and Static Variables

核心模式:全局与静态变量

Protect global/static state with MainActor:
swift
// Swift 6.1: ERROR — non-Sendable type may have shared mutable state
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // Error
}

// Fix: Annotate with @MainActor
@MainActor
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // OK
}
使用MainActor保护全局/静态状态:
swift
// Swift 6.1: 错误——非Sendable类型可能存在共享可变状态
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // 错误
}

// 修复:添加@MainActor注解
@MainActor
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // 正常
}

MainActor Default Inference Mode

MainActor默认推断模式

Swift 6.2 introduces a mode where MainActor is inferred by default — no manual annotations needed:
swift
// With MainActor default inference enabled:
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // Implicitly @MainActor
}

final class StickerModel {
    let photoProcessor: PhotoProcessor
    var selection: [PhotosPickerItem]  // Implicitly @MainActor
}

extension StickerModel: Exportable {  // Implicitly @MainActor conformance
    func export() {
        photoProcessor.exportAsPNG()
    }
}
This mode is opt-in and recommended for apps, scripts, and other executable targets.
Swift 6.2引入了默认推断MainActor的模式——无需手动添加注解:
swift
// 启用MainActor默认推断后:
final class StickerLibrary {
    static let shared: StickerLibrary = .init()  // 隐式标记为@MainActor
}

final class StickerModel {
    let photoProcessor: PhotoProcessor
    var selection: [PhotosPickerItem]  // 隐式标记为@MainActor
}

extension StickerModel: Exportable {  // 隐式标记为@MainActor的协议实现
    func export() {
        photoProcessor.exportAsPNG()
    }
}
该模式为可选启用,推荐在应用、脚本及其他可执行目标中使用。

Core Pattern — @concurrent for Background Work

核心模式:使用@concurrent处理后台任务

When you need actual parallelism, explicitly offload with
@concurrent
:
Important: This example requires Approachable Concurrency build settings — SE-0466 (MainActor default isolation) and SE-0461 (NonisolatedNonsendingByDefault). With these enabled,
extractSticker
stays on the caller's actor, making mutable state access safe. Without these settings, this code has a data race — the compiler will flag it.
swift
nonisolated final class PhotoProcessor {
    private var cachedStickers: [String: Sticker] = [:]

    func extractSticker(data: Data, with id: String) async -> Sticker {
        if let sticker = cachedStickers[id] {
            return sticker
        }

        let sticker = await Self.extractSubject(from: data)
        cachedStickers[id] = sticker
        return sticker
    }

    // Offload expensive work to concurrent thread pool
    @concurrent
    static func extractSubject(from data: Data) async -> Sticker { /* ... */ }
}

// Callers must await
let processor = PhotoProcessor()
processedPhotos[item.id] = await processor.extractSticker(data: data, with: item.id)
To use
@concurrent
:
  1. Mark the containing type as
    nonisolated
  2. Add
    @concurrent
    to the function
  3. Add
    async
    if not already asynchronous
  4. Add
    await
    at call sites
当需要真正的并行执行时,通过
@concurrent
显式卸载任务:
注意: 本示例需启用“易用并发”构建设置——包括SE-0466(MainActor默认隔离)和SE-0461(默认非隔离非发送)。启用后,
extractSticker
会保留在调用方的Actor线程上执行,确保可变状态访问安全。未启用这些设置时,该代码存在数据竞争——编译器会标记错误。
swift
nonisolated final class PhotoProcessor {
    private var cachedStickers: [String: Sticker] = [:]

    func extractSticker(data: Data, with id: String) async -> Sticker {
        if let sticker = cachedStickers[id] {
            return sticker
        }

        let sticker = await Self.extractSubject(from: data)
        cachedStickers[id] = sticker
        return sticker
    }

    // 将耗时任务卸载至并发线程池
    @concurrent
    static func extractSubject(from data: Data) async -> Sticker { /* ... */ }
}

// 调用方需使用await
let processor = PhotoProcessor()
processedPhotos[item.id] = await processor.extractSticker(data: data, with: item.id)
使用
@concurrent
的步骤:
  1. 将包含该函数的类型标记为
    nonisolated
  2. 为函数添加
    @concurrent
    注解
  3. 若函数尚未异步,添加
    async
    关键字
  4. 在调用处添加
    await
    关键字

Key Design Decisions

关键设计决策

DecisionRationale
Single-threaded by defaultMost natural code is data-race free; concurrency is opt-in
Async stays on calling actorEliminates implicit offloading that caused data-race errors
Isolated conformancesMainActor types can conform to protocols without unsafe workarounds
@concurrent
explicit opt-in
Background execution is a deliberate performance choice, not accidental
MainActor default inferenceReduces boilerplate
@MainActor
annotations for app targets
Opt-in adoptionNon-breaking migration path — enable features incrementally
决策内容设计理由
默认单线程执行大多数常规代码无需并发,默认无数据竞争;并发为可选功能
异步函数保留在调用方Actor线程消除导致数据竞争的隐式任务卸载行为
隔离协议实现MainActor类型可实现协议,无需使用不安全的变通方案
@concurrent
显式启用
后台执行是刻意的性能优化选择,而非意外行为
MainActor默认推断减少应用目标中重复的
@MainActor
注解
可选启用非破坏性迁移路径——可逐步启用功能

Migration Steps

迁移步骤

  1. Enable in Xcode: Swift Compiler > Concurrency section in Build Settings
  2. Enable in SPM: Use
    SwiftSettings
    API in package manifest
  3. Use migration tooling: Automatic code changes via swift.org/migration
  4. Start with MainActor defaults: Enable inference mode for app targets
  5. Add
    @concurrent
    where needed
    : Profile first, then offload hot paths
  6. Test thoroughly: Data-race issues become compile-time errors
  1. 在Xcode中启用:进入Build Settings的Swift Compiler > Concurrency板块设置
  2. 在SPM中启用:在包清单中使用
    SwiftSettings
    API
  3. 使用迁移工具:通过swift.org/migration获取自动代码修改工具
  4. 先启用MainActor默认推断:为应用目标启用该模式
  5. 按需添加
    @concurrent
    :先分析性能,再对热点路径进行任务卸载
  6. 全面测试:数据竞争问题会转为编译期错误

Best Practices

最佳实践

  • Start on MainActor — write single-threaded code first, optimize later
  • Use
    @concurrent
    only for CPU-intensive work
    — image processing, compression, complex computation
  • Enable MainActor inference mode for app targets that are mostly single-threaded
  • Profile before offloading — use Instruments to find actual bottlenecks
  • Protect globals with MainActor — global/static mutable state needs actor isolation
  • Use isolated conformances instead of
    nonisolated
    workarounds or
    @Sendable
    wrappers
  • Migrate incrementally — enable features one at a time in build settings
  • 从MainActor开始——先编写单线程代码,再进行性能优化
  • 仅对CPU密集型任务使用
    @concurrent
    ——如图像处理、压缩、复杂计算
  • 为以单线程为主的应用目标启用MainActor推断模式
  • 先分析再卸载任务——使用Instruments工具定位实际性能瓶颈
  • 使用MainActor保护全局状态——全局/静态可变状态需要Actor隔离
  • 使用隔离协议实现——而非
    nonisolated
    变通方案或
    @Sendable
    包装器
  • 逐步迁移——在Build Settings中逐个启用功能

Anti-Patterns to Avoid

适用场景

  • Applying
    @concurrent
    to every async function (most don't need background execution)
  • Using
    nonisolated
    to suppress compiler errors without understanding isolation
  • Keeping legacy
    DispatchQueue
    patterns when actors provide the same safety
  • Skipping
    model.availability
    checks in concurrency-related Foundation Models code
  • Fighting the compiler — if it reports a data race, the code has a real concurrency issue
  • Assuming all async code runs in the background (Swift 6.2 default: stays on calling actor)
  • 所有新建的Swift 6.2+项目(易用并发为推荐默认模式)
  • 将现有Swift 5.x或6.0/6.1项目的并发模型迁移至新版本
  • 在Xcode 26适配过程中解决数据竞争安全相关的编译器错误
  • 构建以MainActor为核心的应用架构(多数UI应用)
  • 性能优化——将特定耗时计算任务卸载至后台

When to Use

  • All new Swift 6.2+ projects (Approachable Concurrency is the recommended default)
  • Migrating existing apps from Swift 5.x or 6.0/6.1 concurrency
  • Resolving data-race safety compiler errors during Xcode 26 adoption
  • Building MainActor-centric app architectures (most UI apps)
  • Performance optimization — offloading specific heavy computations to background