PAYATHON 2026

Xcode 26 中 nonisolated(sending) 与默认隔离设置冲突吗?

技术老齐

两项设置不冲突,两个函数仍是 @MainActor

当 Default Actor Isolation 设置为 MainActor 时,题目中两个没有显式隔离标注的全局函数都会被推断为 @MainActor。同时启用 nonisolated(nonsending) by Default,也不会把它们改成 nonisolated。

这里需要纠正一下设置名称:应为 nonisolated(nonsending) by Default,其中是 nonsending,不是 sending。它对应 Swift 的 NonisolatedNonsendingByDefault 特性,控制非隔离异步函数的默认执行方式。Swift 官方说明

同时启用这两项设置后,题目中的代码在隔离语义上相当于:

@MainActor
func syncMethod() {
    // 可以直接访问 MainActor 隔离的状态
}

@MainActor
func asyncMethod() async {
    // 可以直接访问 MainActor 隔离的状态
}

默认隔离设置不会改变函数的同步或异步性质,第二个函数仍保留 async。

两项设置分别管什么

Default Actor Isolation = MainActor 决定未标注声明的默认 actor 隔离。题目中的全局函数没有其他隔离推断来源,因此同步函数和异步函数都会被推断为 @MainActor。显式声明为 nonisolated 可以退出这项默认规则。SE-0466

nonisolated(nonsending) by Default 作用于已经是 nonisolated 的异步函数:

  • 关闭时,沿用此前的执行语义,在不关联任何 actor 的 generic executor 上执行。
  • 开启时,默认沿用调用方的 actor 执行;需要显式离开 actor 时,可以使用 @concurrent。

这项设置不会自动给所有函数添加 nonisolated,也不改变同步函数的执行规则。SE-0461

默认隔离设置nonisolated(nonsending) by DefaultsyncMethod()asyncMethod()
nonisolated关闭非隔离,同步执行非隔离,在 generic executor 上执行
nonisolated开启非隔离,同步执行非隔离,沿用调用方的 actor
MainActor关闭或开启@MainActor@MainActor

如何按预期声明函数

保持主 actor 隔离

如果函数需要直接读取或修改主 actor 状态,保留当前配置即可。也可以显式写出 @MainActor,让隔离要求不依赖项目默认值:

@MainActor
var count = 0

@MainActor
func syncMethod() {
    count += 1
}

@MainActor
func asyncMethod() async {
    count += 1
}

退出默认主 actor 隔离

如果函数需要是非隔离的,显式添加 nonisolated:

nonisolated
func syncMethod() {
    // 同步执行,不要求调用方位于某个特定 actor
}

nonisolated
func asyncMethod() async {
    // 启用 NonisolatedNonsendingByDefault 时,
    // 默认沿用调用方的 actor 执行
}

异步函数也可以直接写出所需的执行语义:

nonisolated(nonsending)
func asyncMethod() async {
    // 沿用调用方的 actor 执行
}

在支持该语法的 Swift 6.2 编译器中,这种显式写法不受 NonisolatedNonsendingByDefault 是否开启的影响。Swift 官方说明

明确让异步计算离开 actor

要让耗时计算离开调用方的 actor,可以使用 @concurrent:

@concurrent
func calculateTotal(_ values: [Int]) async -> Int {
    values.reduce(0, +)
}

@MainActor
func updateTotal() async {
    let total = await calculateTotal([10, 20, 30])
    // 返回后继续在 MainActor 上执行
    print(total)
}

@concurrent 隐含 nonisolated,可以退出默认主 actor 隔离。它不能与显式 @MainActor 同时用于同一个函数,也不能用于同步函数。SE-0461

容易混淆的地方

“沿用调用方的 actor 执行”和“声明为 @MainActor”有区别。nonisolated(nonsending) 函数可以由不同 actor 调用,不能因为某次调用来自主 actor,就在函数体中直接访问任意 @MainActor 隔离状态。@MainActor 则是声明本身的隔离约束。

async 也不保证在后台执行。启用新规则后,从主 actor 调用的非隔离异步函数可能继续占用主 actor,函数中的长时间同步计算仍可能影响界面响应。

题目对旧行为的描述需要限定前提:按照 Swift 5.7 的 SE-0338 规则,普通非隔离异步函数在 generic executor 上执行。可以用“切到 cooperative thread pool”理解常见运行情况,但判断代码是否安全时,应以 actor 隔离和 executor 语义为准,不能仅凭线程日志。SE-0338

备注:内容仅供参考。