Xcode 26 新功能:Approachable Concurrency 解决并发编译错误

Xcode 26 带来了一个新功能——Approachable Concurrency,苹果说是让并发编程更容易。

之前用 async/await 经常遇到莫名其妙的编译错误,代码逻辑没问题但编译器就是报错,特别是那些 Sendable、隔离相关的问题。用了这个功能后确实好了不少,编译器不会动不动就报错了。

什么是 Approachable Concurrency

Approachable Concurrency 是 Swift 6.2 的一组特性,通过编译器开关 SWIFT_APPROACHABLE_CONCURRENCY 开启。就是让那些不合理的并发编译错误变得更合理。

以前这种代码会报错:

@MainActor
class ViewController: UIViewController {
    func updateUI() {
        // 以前:❌ 编译器报数据竞争错误
        // 现在:✅ 正常通过
        label.text = "更新文本"
    }
}

如何开启 Approachable Concurrency

搜索构建设置里的 "approachable":

Build Settings -> Swift Compiler -> Approachable Concurrency = Yes

Swift Package 需要在 Package.swift 中配置:

swiftSettings: [
  .enableUpcomingFeature("NonisolatedNonsendingByDefault"),
  .enableUpcomingFeature("InferIsolatedConformances"),
  .enableUpcomingFeature("InferSendableFromCaptures"),
  .enableUpcomingFeature("DisableOutwardActorInference"),
  .enableUpcomingFeature("GlobalActorIsolatedTypesUsability"),
]

Xcode 26 新项目默认开启,老项目需要手动设置。建议先在测试项目上试试。

Approachable Concurrency 主要能力

1、nonisolated(nonsending) By Default

nonisolated async 函数现在会在调用者的执行器上运行,而不是全局执行器:

@MainActor
class DataManager {
    privatevar data: [String] = []
    
    nonisolated func processData() async {
        let processed = await performHeavyCalculation()
        await MainActor.run {
            self.data = processed
        }
    }
    
    privatefunc performHeavyCalculation() async -> [String] {
        // 模拟耗时计算
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        return ["Item1", "Item2", "Item3"]
    }
}

2、自动推断 Sendable

编译器能自动判断哪些函数和 KeyPath 是 Sendable:

struct UserModel: Sendable {
    let name: String
    let age: Int
    
    // 编译器自动推断为 Sendable
    func displayName() -> String {
        return "\(name) (\(age)岁)"
    }
}

3、协议一致性隔离推断

如果一个类型是 @MainActor,它的协议实现会自动推断为同样隔离:

@MainActor struct MyData: DataProcessable { // 自动推断为 @MainActor func process() async -> String { return "处理后的数据" } }

4、减少 nonisolated(unsafe) 使用

编译器能更智能地判断什么时候数据传递是安全的,减少手动标记 nonisolated(unsafe)。

5、禁用向外隔离推断

属性包装器不再有"传染性"行为,比如 @StateObject 不会让整个 View 变成 @MainActor。

Approachable Concurrency 实际体验

编译错误明显减少了,特别是那些"看起来没问题但编译器报错"的情况。

以前写 SwiftUI + async/await 经常要这样:

Button("加载数据") {
    Task { @MainActor in
        await viewModel.loadData()
    }
}

现在可以直接:

Button("加载数据") {
    Task {
        await viewModel.loadData()
    }
}

对新手比较友好,不用过多关心隔离规则。

我的笔记