使用 properyWrapper 实现 UserDefaults 会导致奇怪的问题
Implementing UserDefaults using properyWrapper causes weird problem
我正在尝试使用 @propertyWrapper
实现 UserDefaults class。我想要做的是为我的应用程序用户首选项创建一个包装器 class。所以我写了下面的代码。
@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
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 class 也在关注。
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
会导致一些内存问题。所以我不想评论它。我错过了什么,我该如何解决?
方法observe(change: @escaping (T?, T?)
初始化DefaultsObservation
对象和returns值。没有对该对象的任何强引用,所以这就是它被释放并调用 deInit
的原因。你需要保持这个对象的强引用。例如
var valueObserver: NSObject? = nil
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
valueObserver = AppData.$layout.observe { old, new in
print(old)
}
}
我正在尝试使用 @propertyWrapper
实现 UserDefaults class。我想要做的是为我的应用程序用户首选项创建一个包装器 class。所以我写了下面的代码。
@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 class 也在关注。
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
会导致一些内存问题。所以我不想评论它。我错过了什么,我该如何解决?
方法observe(change: @escaping (T?, T?)
初始化DefaultsObservation
对象和returns值。没有对该对象的任何强引用,所以这就是它被释放并调用 deInit
的原因。你需要保持这个对象的强引用。例如
var valueObserver: NSObject? = nil
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
valueObserver = AppData.$layout.observe { old, new in
print(old)
}
}