PAYATHON 2026

Xcode 中 Approachable concurrency 与其他并发设置会冲突吗?

技术老齐

不会冲突,显式设置的单项值可以覆盖组合开关的默认值

按 Xcode 26、Swift 6.2 的设置机制,题目中的配置可以成立:

设置值实际作用
Approachable ConcurrencyYes为一组并发特性提供启用的默认值
nonisolated(nonsending) By Default显式设为 No不默认采用 nonisolated(nonsending)
Default Actor Isolationnonisolated不默认推断 @MainActor 隔离

没有其他编译参数干预时,普通的 nonisolated async 函数仍采用原有行为:离开调用方的 actor,在通用执行器(generic executor)上执行。组合开关启用的其他特性,只要没有被单独覆盖,就仍然生效。

Swift 的 approachable concurrency 整体设计包含多项改进,Xcode 中同名的构建设置只控制其中一组特性,不会把 Default Actor Isolation 改成 MainActor。Apple 构建设置参考

三项设置分别控制什么

Approachable Concurrency 提供一组默认配置

Apple 列出的特性包括:

  • DisableOutwardActorInference
  • GlobalActorIsolatedTypesUsability
  • InferIsolatedConformances
  • InferSendableFromCaptures
  • NonisolatedNonsendingByDefault

这些特性涉及隔离推断、协议遵循、Sendable 推断以及非隔离异步函数的执行语义。组合开关可以统一启用这些改进,也可以通过独立设置逐项调整。Apple 构建设置参考

在 Swift Build 的 Swift 6.2 发布版配置中,NonisolatedNonsendingByDefault 对应设置的默认值来自:

DefaultValue = "$(SWIFT_APPROACHABLE_CONCURRENCY)";

单项值为 YES 时,会生成启用该特性的编译参数;为 NO 时,则不生成。因此,显式设为 No 可以覆盖组合开关提供的默认值。Swift Build 配置源码

Default Actor Isolation 决定声明的默认隔离

当声明没有显式隔离标注,也没有其他隔离推断来源时,这项设置决定采用哪种隔离:

  • MainActor:符合规则的声明默认推断为 @MainActor。
  • nonisolated:不默认添加 @MainActor 隔离。

显式的 @MainActor、actor 自身的隔离,以及从父类或协议遵循等位置推断出的隔离,仍按各自规则处理。设为 nonisolated 不会清除这些隔离。SE-0466

nonisolated(nonsending) By Default 决定非隔离异步函数如何执行

这项设置控制 nonisolated async 函数的默认执行语义:

单项最终值默认执行语义
Yes保留调用方的 actor 隔离;调用方没有 actor 隔离时,也不会因此获得 actor 隔离
No离开调用方的 actor,在通用执行器上执行

因此,默认声明为 nonisolated,同时让非隔离异步函数保留调用方隔离,两者可以成立。函数没有固定的 MainActor 隔离,但从 MainActor 调用时,可以继续在那里执行。SE-0461

如何配置并确认结果

要采用题目中的组合,可以在目标的 Build Settings 中显式设置,也可以使用对应的 .xcconfig:

SWIFT_APPROACHABLE_CONCURRENCY = YES
SWIFT_UPCOMING_FEATURE_NONISOLATED_NONSENDING_BY_DEFAULT = NO
SWIFT_DEFAULT_ACTOR_ISOLATION = nonisolated

配置后,按以下步骤检查:

  1. 选择实际编译代码的 target,分别检查 Debug 和 Release。

  2. 在 Build Settings 的 Levels 视图中查看最终解析值,确认单项设置已被显式覆盖。

  3. 展开构建日志中的 Swift 编译命令,检查其他配置是否仍传入了以下参数:

    -enable-upcoming-feature NonisolatedNonsendingByDefault
    

按上述配置,该单项设置不应生成这个启用参数。不过,No 的作用仅是不生成参数。如果 Other Swift Flags 等位置另行加入了同一个参数,还需要处理相应的配置来源。Swift Build 配置源码

代码示例:默认行为与显式行为

以下示例使用 Swift 6.2 支持的语法:

nonisolated func useDefaultBehavior() async {
    // 执行语义取决于 NonisolatedNonsendingByDefault 的最终值。
}

nonisolated(nonsending) func stayWithCaller() async {
    // 显式保留调用方的 actor 隔离。
}

@concurrent
func leaveCallerActor() async {
    // 显式切换到通用执行器。
}

@MainActor
func refresh() async {
    await useDefaultBehavior()
    await stayWithCaller()
    await leaveCallerActor()
}

在题目的配置下:

  • useDefaultBehavior() 离开 MainActor 执行。
  • stayWithCaller() 保留调用方的 MainActor 隔离。
  • leaveCallerActor() 离开 MainActor 执行。

如果只把 nonisolated(nonsending) By Default 改为 Yes,useDefaultBehavior() 也会保留调用方隔离。两个显式标注的函数不受这个默认值变化的影响。SE-0461

注意事项

nonisolated(nonsending) 描述的是调用时如何保留隔离。它不等于 @MainActor,也不会让函数自动获得访问任意 actor 私有状态的权限。

async 和 await 本身不保证把耗时计算移出主 actor。如果非隔离异步函数保留了调用方隔离,从 MainActor 调用时,函数中长时间运行的同步计算仍可能阻塞界面。需要明确离开 actor 执行的异步计算,可以使用 @concurrent。SE-0461

上述结论以 Swift 6.2 的特性开关机制为前提。更换工具链或语言模式后,应重新检查最终编译参数。已经成为语言模式固定行为的特性,不能仅靠将对应设置改为 No 来恢复旧语义。默认隔离按模块生效,应用 target 的设置也不会改写依赖模块中声明的隔离。Apple 构建设置参考、SE-0466

备注:内容仅供参考。