该错误警告您,您的类公开了一个可变属性.该可变属性可以从SWIFT并发的外部访问,因此不安全.
请考虑以下事项:
final class Foo: Sendable {
@MainActor var counter = 0 // Stored property 'counter' of 'Sendable'-conforming class 'Foo' is mutable
}
无论如何,我们现在可以考虑与counter
直接交互的视图控制器的以下属性和方法:
let foo = Foo()
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.counter += 1
}
print(foo.counter) // 6146264 !!!
}
}
注:如果您已将"SWIFT并发判断"版本设置设置为"Minimal"或"Targeted",则编译上述代码时将只显示上述警告.(如果您将其更改为"Complete",它将变成一个硬错误.)
总之,简而言之,如果您已经将标记为@MainActor
,但没有任何东西可以阻止其他线程直接与该类的这个属性交互.
如果您要让一个具有可变属性的非参与者BESendable
,您必须自己实现线程安全.例如:
final class Foo: @unchecked Sendable {
private var _counter = 0
private let queue: DispatchQueue = .main // I would use `DispatchQueue(label: "Foo.sync")`, but just illustrating the idea
var counter: Int { queue.sync { _counter } }
func increment() {
queue.sync { _counter += 1 }
}
}
和
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.increment()
}
print(foo.counter) // 10000000
}
}
显然,您也可以将自己限制为不可变的属性,而不需要同步.但我想你需要变化无常.
Now, in this mutable scenario, you can use whatever synchronization mechanism you want, but hopefully this illustrates the idea. In short, if you are going to allow it to mutate outside of Swift concurrency, you have to implement the synchronization yourself. 和 because we are implementing our own synchronization, we tell the compiler that it is @unchecked
, meaning that you are not going to have the compiler check it for correctness, but rather that burden falls on your shoulders.
显然,如果你使用一个演员,并保持在SWIFT并发的世界中,生活就会容易得多.例如:
actor Bar {
var counter = 0
func increment() {
counter += 1
}
}
和:
let bar = Bar()
func incrementBarManyTimes() {
Task.detached {
await withTaskGroup(of: Void.self) { group in
for _ in 0 ..< 10_000_000 {
group.addTask { await self.bar.increment() }
}
await print(self.bar.counter)
}
}
}