【问题标题】:Implementing UserDefaults using properyWrapper causes weird problem使用properyWrapper 实现UserDefaults 会导致奇怪的问题
【发布时间】:2020-06-19 15:20:18
【问题描述】:

我正在尝试使用@propertyWrapper 实现 UserDefaults 类。我想要做的是为我的应用用户偏好创建一个包装类。所以我写了以下代码。

@propertyWrapper
struct Storage<T> {
    private let key: String
    private let defaultValue: T
    var projectedValue: Storage<T> { return self }
    var wrappedValue: T {
        get {
            return UserDefaults.standard.string(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }

    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    func observe(change: @escaping (T?, T?) -> Void) -> NSObject {
        return DefaultsObservation(key: key) { old, new in
            change(old as? T, new as? T)
        }
    }
}

然后,我想观察一下 UserDefaults 值的变化。所以我实现了一个名为DefaultsObservation的观察类。

class DefaultsObservation: NSObject {
    let key: String
    private var onChange: (Any, Any) -> Void

    init(key: String, onChange: @escaping (Any, Any) -> Void) {
        self.onChange = onChange
        self.key = key
        super.init()
        UserDefaults.standard.addObserver(self, forKeyPath: key, options: [.old, .new], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        guard let change = change, object != nil, keyPath == key else { return }
        onChange(change[.oldKey] as Any, change[.newKey] as Any)
    }

    deinit {
        UserDefaults.standard.removeObserver(self, forKeyPath: key, context: nil)
    }
}

我的 AppData 课程也在关注。

struct AppData {
    @Storage(key: "layout_key", defaultValue: "list")
    static var layout: String
}

但是,当我尝试添加和侦听更改 layout 属性时,它无法正常工作。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    AppData.$layout.observe { old, new in
        print(old)
    }
}

当我调试它时,deinit 在调用viewWillAppear 方法后立即工作。当我注释掉 deinit 删除观察者的方法时,一切都很完美。我认为关闭deinit 会导致一些内存问题。所以我不想评论它。我错过了什么,我该如何解决?

【问题讨论】:

  • 可能不相关,但存在类型错误。 string(forKey:UserDefaults 中总是返回String(或nil)。为了更通用,你必须写object(forKey:
  • 谢谢我考虑写一个更通用的。

标签: ios swift key-value-observing property-wrapper


【解决方案1】:

方法observe(change: @escaping (T?, T?)初始化DefaultsObservation对象并返回值。没有对这个对象的任何强引用,所以这就是它被释放并调用deInit 的原因。您需要保持对该对象的强引用。例如

var valueObserver: NSObject? = nil
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    valueObserver = AppData.$layout.observe { old, new in
        print(old)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    • 2012-04-07
    • 2020-02-05
    • 2013-02-08
    • 2021-07-31
    • 1970-01-01
    • 2019-06-08
    相关资源
    最近更新 更多