协议继承中出现的意外行为

移动开发 2026-07-09

我在项目设置中默认使用主actor,并使用Swift 6模式。下面是代码:

public protocol Main {}
nonisolated public protocol NonIso {}

// Foo is on main
// As proven by the compilation failure in `testNonIso()`
public protocol Foo: NonIso, Main {
  func foo()
}

nonisolated func testNonIso() {
  let f: Foo! = nil
  // Call to main actor-isolated instance method 'foo()' in a synchronous nonisolated context
  f.foo()
}

正如我的 testNonIso() 函数所验证的,Foo 协议被限定在主actor上。

接着我把 Foo 标记为非隔离,错误就消失了:

public protocol Main {}

nonisolated public protocol NonIso {}

// Foo is on nonisolated
// As proven by no compilation error in `testNonIso()`
nonisolated public protocol Foo: NonIso, Main {
  func foo()
}

nonisolated func testNonIso() {
  let f: Foo! = nil
  // this compiles fine, since Foo is non-isolated
  f.foo()
}

以上都说得通。

接下来情况会有点怪——不是使用 NonIso 协议,而是使用 Codable 协议,它也是非隔离的:

public protocol Main {}

// Now `Foo` becomes non-isolated for some reason. In the previous example Foo was main isolated. 
// This is verified by the compilation error when conforming struct `F` to `Foo`
public protocol Foo: Codable, Main {
  func foo()
}

// Conformance of 'F' to protocol 'Foo' crosses into main actor-isolated code and can cause data races
struct F: Foo {
  func foo() {}
}

上面的代码给出编译错误,表明这次 Foo 变成了非隔离。

现在把 Foo 标记为 @MainActor

public protocol Main {}
nonisolated public protocol NonIso {}

// Still non-isolated, despite of @MainActor here
@MainActor public protocol Foo: Codable, Main {
  func foo()
}

// Conformance of 'F' to protocol 'Foo' crosses into main actor-isolated code and can cause data races
struct F: Foo {
  func foo() {}
}

我得到完全相同的错误,意味着即便我把 Foo 标记为主actor,它仍然是非隔离的。

现在再做一次确认,开个玩笑,把上面的代码中的 Codable 重新替换回 NonIso,编译错误就消失了:

public protocol Main {}
nonisolated public protocol NonIso {}

// Foo is main isolated, with or without @MainActor
@MainActor public protocol Foo: NonIso, Main {
  func foo()
}

// Compiles fine this time
struct F: Foo {
  func foo() {}
}

我想知道我的 NonIso 协议与 Codable 协议之间有什么区别导致这种行为?

我在使用Xcode 26.2

解决方案

Foo 改为从 Codable 继承之后,你说

上面的代码给出编译错误,表明这次 Foo 变成了非隔离。

这不是真的。Foo 仍然是主actor隔离的。编译器错误涉及的是 Codable 的要求,它们是非隔离的。如果 Foo 真的是非隔离的,我不应该能够用 @MainActor 方法来满足它的 foo() 要求,但我确实可以!

// Just to make it very clear that this is not due to an isolated conformance to Foo, 
// we write 'nonisolated Foo' explicitly to disable any inference of isolated conformance
struct F: nonisolated Foo {
    // this compiles!
    @MainActor
    func foo() {}

    // these are required to be non-isolated by Codable
    nonisolated init(from decoder: any Decoder) throws {
    }

    nonisolated func encode(to encoder: any Encoder) throws {
    }
}

毕竟,@MainActor 协议仍然可以有非隔离的要求。Foo 实际上只有一个主actor隔离的要求 foo,以及两个非隔离的要求 init(from:)encode(to:)

与其把 protocol Foo: Codable, Main 读作“Foo 继承自 CodableMain”,不如把它理解为“实现 Foo 还需要同时实现 CodableMain”。

之所以 NonIso 情况能编译,是因为 NonIso 没有任何要求,因此 F 对它是符合的(因此也符合 Foo),即使 F 是主actor隔离的。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章