我想创建一个property wrapper
,它将存储一个callback block
,并在每次值更改时执行一次。简单的KVO
之类的东西。它工作正常,但是有一个问题。如果我在此回调块中使用属性本身,则会收到错误消息:
Simultaneous accesses to 0x6000007fc3d0, but modification requires exclusive access
据我了解,这是因为在执行此块时仍在写入属性本身,这就是为什么无法读取它的原因。
让我们添加一些代码,以显示我的意思:
@propertyWrapper
struct ReactingProperty<T> {
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
public var wrappedValue: T {
didSet {
reaction?(wrappedValue)
}
}
public var projectedValue: Self {
get { self }
set { self = newValue }
}
private var reaction: ((_ value: T) -> Void)?
public mutating func setupReaction(_ reaction: @escaping (_ value: T) -> Void) {
self.reaction = reaction
}
}
还有AppDelegate
:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ReactingProperty
var testInt = 0
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 1. This will pass correctly.
$testInt.setupReaction { (value) in
print(value)
}
testInt = 1
// 2. This will cause crash, because I access testInt in this block, that is executed when value changes.
$testInt.setupReaction { [unowned self] (value) in
print(self.testInt)
}
testInt = 2
return true
}
}
为此,我有一些解决方法,但是出于各种原因,我实际上都不需要主题。
如果我改为从此block
访问block argument
中的值,并将此参数传递给值didSet
,则它可以正常工作。但这迫使我总是以这种方式使用它,并且我想在包含各种其他回调的代码中使用它,有时对我来说,直接访问此值也更方便。 p>
我可以callback block
异步执行(DispachQueue.main.async { self.block?(value) })
。但这也不是我的最佳选择。
改为使用combine
。我可能会,但我现在也想保留此功能。我也只是对这个问题感到好奇。
这可以克服吗?究竟是什么变量导致此read-write
访问错误?这是propertyWrapper
内的值还是propertyWrapper
本身的结构?
我认为是propertyWrapper struct
访问导致此问题,而不是其内部价值,但我不确定。
答案 0 :(得分:0)
我认为我找到了正确的解决方案。只是从结构更改为类。那么读/写访问就没有问题。
@propertyWrapper
class ReactingProperty<T> {
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
public var wrappedValue: T {
didSet {
reaction?(wrappedValue)
}
}
public lazy var projectedValue = self
private var reaction: ((_ value: T) -> Void)?
public mutating func setupReaction(_ reaction: @escaping (_ value: T) -> Void) {
self.reaction = reaction
}
}