我有这个代码:
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 并发的实验代码。
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
}