Swift,演员:演员隔离的属性“扫描”不能从非隔离的上下文中改变

zum*_*zum 39 actor swift swift-concurrency

我有一个演员:

actor StatesActor {

    var job1sActive:Bool = false
    ...

}
Run Code Online (Sandbox Code Playgroud)

我有一个使用该演员的对象:

class MyObj {
    
    let myStates = StatesActor()
    
    func job1() async {
    
        myStates.job1IsActive = true

    }
}
Run Code Online (Sandbox Code Playgroud)

线:

myStates.job1IsActive = true

出现以下错误:

演员隔离属性“job1IsActive”无法从非隔离上下文中发生突变

如何使用 actor 正确存储/读取状态信息,以便 MyObj 可以使用它来读取和设置状态?

mat*_*att 45

如何使用 actor 正确存储/读取状态信息,以便 MyObj 可以使用它来读取和设置状态?

您无法从 Actor 外部更改 Actor 的实例变量。这就是演员的全部意义!

相反,为参与者提供一个设置其自己的实例变量的方法。然后您将能够调用该方法(使用)。await

  • `await MainActor.run { ...在这里设置你可爱的属性...}` (11认同)
  • 异步属性设置器在理论上是可能的。Swift 只是还没有它们。 (7认同)
  • 我有一个带有 @MainActor 属性的非演员模型,所以这有效:``Task {@MainActor in CODE }``` (2认同)

dav*_*sdk 6

不允许使用未在参与者上运行的代码中的属性设置器(确切的术语是“参与者隔离代码”)。改变 Actor 状态的最佳位置是 Actor 本身内部的代码。在你的情况下:

actor StatesActor {
    var job1IsActive: Bool = false
    func startJob1() {
        job1IsActive = true
        ...
    }
}
class MyObj {
    let myStates = StatesActor()
    func job1() async {
        await myStates.startJob1()
    }
}
Run Code Online (Sandbox Code Playgroud)

异步属性设置器在理论上是可能的,但不安全。这可能就是 Swift 没有它们的原因。编写类似if await actor.a == nil { await actor.a = "Something" }, where actor.acan 在对 getter 和 setter 的调用之间进行更改的内容会变得太容易了。

上面的答案在 99% 的情况下都有效(对于所提出的问题来说已经足够了)。但是,在某些情况下,需要从 Actor 外部的代码更改 Actor 的状态。对于MainActor,使用MainActor.run(body:). 对于全局参与者,@NameOfActor可以应用于函数(并且可以用于定义run类似于 的函数MainActor)。在其他情况下,isolated关键字可以用在函数的 actor 参数前面:

func job1() async {
    await myStates.startJob1()
    let update: (isolated StatesActor) -> Void = { states in
        states.job1IsActive = true
    }
    await update(myStates)
}
Run Code Online (Sandbox Code Playgroud)