原子 属性 包装器仅在声明为 class 时有效,而不是结构
Atomic property wrapper only works when declared as class, not struct
我在 Swift 中创建了一个“锁”,并为我的 Swift classes 创建了一个使用该锁的 Atomic 属性 包装器 Swift 缺少 ObjC 的 atomic
属性 属性。
当我 运行 我的测试启用了线程清理器时,它总是在使用我的 Atomic 属性 包装器的 属性 上捕获数据竞争。
唯一有效的方法是将 属性 包装器的声明更改为 class 而不是结构,这里的主要问题是:为什么它有效!
我在 属性 包装器中添加了 print
s 并锁定 init
s 以跟踪创建的对象数量,与 struct/class 相同,已尝试在另一个项目中重现该问题,也没有用。但我会添加与问题相似的文件,并让我知道任何关于 为什么它有效的猜测。
锁定
public class SwiftLock {
init() { }
public func sync<R>(execute: () throws -> R) rethrows -> R {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
return try execute()
}
}
原子 属性 包装器
@propertyWrapper struct Atomic<Value> {
let lock: SwiftLock
var value: Value
init(wrappedValue: Value, lock: SwiftLock=SwiftLock()) {
self.value = wrappedValue
self.lock = lock
}
var wrappedValue: Value {
get {
lock.sync { value }
}
set {
lock.sync { value = newValue }
}
}
}
模型(数据竞争应该发生在publicVariable2
属性这里)
class Model {
@Atomic var publicVariable: TimeInterval = 0
@Atomic var publicVariable2: TimeInterval = 0
var sessionDuration: TimeInterval {
min(0, publicVariable - publicVariable2)
}
}
更新 1:
完整 Xcode 个项目:https://drive.google.com/file/d/1IfAsOdHKOqfuOp-pSlP75FLF32iVraru/view?usp=sharing
这个 PR 中回答了这个问题:https://github.com/apple/swift-evolution/pull/1387
我认为这是真正解释它的那些台词
In Swift's formal memory access model, methods on a value types are considered to access the entire value, and so calling the wrappedValue getter formally reads the entire stored wrapper, while calling the setter of wrappedValue formally modifies the entire stored wrapper.
The wrapper's value will be loaded before the call to
wrappedValue.getter
and written back after the call to
wrappedValue.setter
. Therefore, synchronization within the wrapper
cannot provide atomic access to its own value.
我在 Swift 中创建了一个“锁”,并为我的 Swift classes 创建了一个使用该锁的 Atomic 属性 包装器 Swift 缺少 ObjC 的 atomic
属性 属性。
当我 运行 我的测试启用了线程清理器时,它总是在使用我的 Atomic 属性 包装器的 属性 上捕获数据竞争。
唯一有效的方法是将 属性 包装器的声明更改为 class 而不是结构,这里的主要问题是:为什么它有效!
我在 属性 包装器中添加了 print
s 并锁定 init
s 以跟踪创建的对象数量,与 struct/class 相同,已尝试在另一个项目中重现该问题,也没有用。但我会添加与问题相似的文件,并让我知道任何关于 为什么它有效的猜测。
锁定
public class SwiftLock {
init() { }
public func sync<R>(execute: () throws -> R) rethrows -> R {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
return try execute()
}
}
原子 属性 包装器
@propertyWrapper struct Atomic<Value> {
let lock: SwiftLock
var value: Value
init(wrappedValue: Value, lock: SwiftLock=SwiftLock()) {
self.value = wrappedValue
self.lock = lock
}
var wrappedValue: Value {
get {
lock.sync { value }
}
set {
lock.sync { value = newValue }
}
}
}
模型(数据竞争应该发生在publicVariable2
属性这里)
class Model {
@Atomic var publicVariable: TimeInterval = 0
@Atomic var publicVariable2: TimeInterval = 0
var sessionDuration: TimeInterval {
min(0, publicVariable - publicVariable2)
}
}
更新 1: 完整 Xcode 个项目:https://drive.google.com/file/d/1IfAsOdHKOqfuOp-pSlP75FLF32iVraru/view?usp=sharing
这个 PR 中回答了这个问题:https://github.com/apple/swift-evolution/pull/1387
我认为这是真正解释它的那些台词
In Swift's formal memory access model, methods on a value types are considered to access the entire value, and so calling the wrappedValue getter formally reads the entire stored wrapper, while calling the setter of wrappedValue formally modifies the entire stored wrapper.
The wrapper's value will be loaded before the call to
wrappedValue.getter
and written back after the call towrappedValue.setter
. Therefore, synchronization within the wrapper cannot provide atomic access to its own value.