Xcode 中 Approachable concurrency 与其他并发设置会冲突吗?
不会冲突,显式设置的单项值可以覆盖组合开关的默认值
按 Xcode 26、Swift 6.2 的设置机制,题目中的配置可以成立:
| 设置 | 值 | 实际作用 |
|---|---|---|
Approachable Concurrency | Yes | 为一组并发特性提供启用的默认值 |
nonisolated(nonsending) By Default | 显式设为 No | 不默认采用 nonisolated(nonsending) |
Default Actor Isolation | nonisolated | 不默认推断 @MainActor 隔离 |
没有其他编译参数干预时,普通的 nonisolated async 函数仍采用原有行为:离开调用方的 actor,在通用执行器(generic executor)上执行。组合开关启用的其他特性,只要没有被单独覆盖,就仍然生效。
Swift 的 approachable concurrency 整体设计包含多项改进,Xcode 中同名的构建设置只控制其中一组特性,不会把 Default Actor Isolation 改成 MainActor。Apple 构建设置参考
三项设置分别控制什么
Approachable Concurrency 提供一组默认配置
Apple 列出的特性包括:
DisableOutwardActorInferenceGlobalActorIsolatedTypesUsabilityInferIsolatedConformancesInferSendableFromCapturesNonisolatedNonsendingByDefault
这些特性涉及隔离推断、协议遵循、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
配置后,按以下步骤检查:
-
选择实际编译代码的 target,分别检查 Debug 和 Release。
-
在 Build Settings 的 Levels 视图中查看最终解析值,确认单项设置已被显式覆盖。
-
展开构建日志中的 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
备注:内容仅供参考。
