使用PropertyWrapper的同时访问

问题描述 投票:0回答:1

我想创建一个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
    }

}

我有一些解决方法,但是出于各种原因,我实际上并不需要主题。

  1. 如果我改为从此block访问block argument中的值,并将此参数传递给值didSet,则它可以正常工作。但是,这迫使我始终以这种方式使用它,并且我想将其与包含各种其他回调的代码一起使用,有时对于我来说,也能够直接访问此值更加方便。

  2. 我可以异步执行callback block (DispachQueue.main.async { self.block?(value) })。但这也不是我的最佳选择。

  3. 改用combine。我可能会,但我现在也想保留此功能。我也只是对这个问题感到好奇。

可以通过某种方式克服吗?到底是什么变量导致此read-write访问错误?这是propertyWrapper内部的值还是propertyWrapper本身的结构?我认为是propertyWrapper struct访问导致此问题,而不是其内部值,但我不确定。

ios swift thread-safety read-write property-wrapper
1个回答
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
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.