为什么我可以在 Swift 中访问 actor 的 init 中的隔离 ivars

问题描述 投票:0回答:1

我有这个代码:

actor Actor {
  
  @MainActor
  var checker: @MainActor @Sendable () -> Void = {}
  
  init(checker: @escaping @Sendable () -> Void) {
    self.checker = checker
    _ = self.checker // access it in nonisolated context
  }
  
  nonisolated func noniso() {
    Task { @MainActor in
      _ = self.checker
    }
  }
}

我的理解是,init 是非隔离的,因为我可以在非隔离上下文中创建 actor:

let actor = Actor {}

但是,在非隔离函数

noniso
中,我必须使用
@MainActor
才能访问
self.checker
。否则会出现编译错误。

为什么我可以在 init 中的这个非隔离上下文中访问这个隔离的 ivar

checker

请不要评判我为什么在 actor 中放一个

@MainActor
ivar 等。这只是我学习 swift 并发的实验代码。

ios swift concurrency
1个回答
0
投票

Actor 中的非异步初始化器确实没有与 Actor 隔离,因为它们具有同步性质 - Actor 跳跃无法完成。

但是,编译器可以证明在初始化程序中访问

self.checker
是安全的。在初始化程序执行期间,没有其他线程可以访问正在初始化的对象
self
,除非初始化程序将
self
传递到其他地方。

由于

checker
是孤立的主要参与者,一个很好的例子就是尝试将
self
传递给
@MainActor
函数。

actor SomeActor {
    
    @MainActor
    var checker: @MainActor @Sendable () -> Void = {}
    
    init(checker: @escaping @Sendable () -> Void) {
        self.checker = checker
        foo(self)
        self.checker = { /* something */ }
    }
}


@MainActor
func foo(_ a: SomeActor) {
    // now foo has access to 'self.checker' too!
    a.checker = { /* something else */ }
}

这里编译器正确地给你一个错误。如果编译器允许这样做,

self.checker = { /* something */ }
a.checker = { /* something else */ }
可能会在不同的线程上运行,天知道最终会写入
checker
什么。

另一个例子是在主角身上运行的

self
中捕获
Task

init(checker: @escaping @Sendable () -> Void) {
    self.checker = checker
    // comment out the below line and the error is gone
    Task { @MainActor in self.checker = { /* something */ } }
    print(self.checker) // error here
}
© www.soinside.com 2019 - 2024. All rights reserved.