如何在保持隔离的同时符合协议要求,将Swift actor符合协议?

15
假设我们有以下目标:
  1. 我们想使用actors。
  2. 我们想使用依赖注入(Dependency Injection),并通过DI解析actors。
  3. 我们想将actors隐藏在协议后面,以便我们可以变化实现方式。
  4. 我们想保持actor隔离,否则使用actors就没有意义。
因此,具体问题是:如何在保持隔离的同时符合Swift actor到协议的规范?
protocol Zippity {
  func foo()
}

actor Zappity: Zippity {
  func foo() {} // Doesn't compile
}

我可以使用……使其编译通过。

actor Zappity: Zippity {
  nonisolated func foo() {}
}

... 但这似乎违背了初衷。我也尝试过声明接口方法为async,但它也无法编译。

我可以想到几种合理的解决方法,包括组合、调用隔离方法的nonisolated async方法等,但我想知道是否有我遗漏的内容。

3个回答

30

好的,我已经找到了答案,而且非常简单:

protocol Zippity: Actor {
  func foo()
}

actor Zappity: Zippity {
  func foo() // This is a properly isolated method
}

看起来声明符合 Actor 协议启用了编译器的魔法。


顺便问一下,你知道有没有一种方法可以在不在那个类上实现“Actor”协议的情况下与一个类共享该协议?因为如果我还需要一个非actor类(用于我的旧代码)在同一个代码库中,我会收到一个错误,如:“Type 'Zeppity' does not conform to protocol 'Actor'”。 - Ricardo Barroso
1
很遗憾,如果一个Actor要符合协议(protocol)但不添加nonisolated关键词,这最终会破坏Actor的设计思想,所以你需要在函数中添加async关键词。如果你的遗留代码是用Swift写的,那么这个方法可能有效。但如果是用obj-c写的,你可能会遇到一些麻烦。我建议你要么构建两个不同的协议,要么使用封装器将其转换为Tasks。 - Pedro Farina
谢谢@PedroFarina。与此同时,我按照你的建议之一,采用了两种不同的协议来解决问题。 - Ricardo Barroso

1
只要协议不使用属性设置器,将每个方法和属性 getter 标记为 async 将允许 actor 符合该协议。由于不能有异步设置器,在这种情况下需要符合 Actor 协议。
因此在您的示例中:
protocol Zippity {
    func foo() async
}

1

通过使所有函数隐式异步化,演员才能保证线程安全。它在幕后进行一些魔法,以确保它在正确的线程中被调用。

要解决这个问题,您可以从协议上继承Actor。

protocol Foo: Actor {
func bar()
}

或者将函数标记为异步

protocol Foo {
func bar() async
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接