ios - 使用 PropertyWrapper 时同时访问

标签 ios swift thread-safety read-write property-wrapper

我想创建一个属性包装器,它将存储一个回调 block ,并在每次值发生变化时执行它。像简单的KVO之类的东西。它工作得很好,但有一个问题。如果我在此回调 block 中使用属性本身,则会收到错误:

Simultaneous accesses to 0x6000007fc3d0, but modification requires exclusive access

据我了解,这是因为在执行该 block 时,属性本身仍在被写入,这就是无法读取它的原因。

让我们添加一些代码,以展示我的意思:

@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 argument 访问 block 中的值,并在值 didSet 中传递这个参数,那么效果很好。但这迫使我总是以这种方式使用它,我想将其与包含各种其他回调的代码一起使用,有时我也能够直接访问这个值会更方便。

  2. 我可以异步执行回调 block (DispachQueue.main.async { self.block?(value) })。但这也不是最适合我的情况。

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

这个问题可以通过某种方式克服吗?哪个变量实际上导致了此读写访问错误?这是 propertyWrapper 内部的值还是带有 propertyWrapper 本身的结构? 我认为是 propertyWrapper struct 访问导致了这种情况,而不是其内部值,但我不确定。

最佳答案

我想我已经找到了正确的解决方案。只需从结构更改为类即可。那么读/写访问就没有问题了。

@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
    }

}

关于ios - 使用 PropertyWrapper 时同时访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59317936/

相关文章:

ios - JSON 未在 TableView 中加载

c# - 不可变类型的实例如何本质上是线程安全的

ios - iBeacon 接收器无法识别传输的信号

ios - 如何快速构建具有不同原型(prototype)单元的 UITableViewController

ios - SwiftUI 闭包捕获 @State 会破坏内存?

arrays - 加入多维数组

ios - 从字典数组中删除

python - tkinter 的 `after` 方法线程安全吗?

java - 单次和双重检查延迟初始化

ios - 如何更改屏幕上尚未显示的单元格的属性?