iOS KVO - 检测何时再次设置相同的值

iOS KVO - detect when the same value is set again

是否可以通过一种方式使用 KVO,它不仅可以检测值是否更改,还可以检测是否再次设置了相同的值?我目前仅在值更改(与之前设置的不同)时才收到通知。每次设置值时我都需要收到通知(即使它与之前设置的值相同)。我怎样才能做到这一点?

我的代码:

private func addObserver() {
    defaults.addObserver(self, forKeyPath: DefaultsKeys.testKey._key, options: .new, context: nil)
}

public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard let value = change?[NSKeyValueChangeKey.newKey] as? Bool else { return }
    statusCallback?(value)
}

private func removeObserver() {
    defaults.removeObserver(self, forKeyPath: DefaultsKeys.testKey._key)
}

您可以通过以下方式实现:

public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
   let value = change?[.oldKey] as? Bool
   guard value == nil || value != yourVariableToCheck else { return }
   statusCallback?(value)
}

仅在您自己的变量上更改 yourVariableToCheck

KVO 机制非常简单 - 它不会在设置新值时执行任何额外检查,它仅在调用 setter 时触发。因此,无法区分该值是否与已设置的值不同。 这很好。首先,因为在实践中,将相同的值赋给一个变量并不常见。
其次,引入额外的检查是消耗品,在大多数情况下是不需要的。如果存在该检查,则会对性能产生负面影响。

也就是说,就 Swift 而言,您可以考虑用本地 Swift 属性 观察者替换 KVO 机制(本质上是 Objective-C 遗留物) : willSetdidSet。这基本上与传递两个选项所起的作用相同:NSKeyValueObservingOptionNewNSKeyValueObservingOptionOld(Swift 中的 .old.new)到 addObserver方法。一旦指定了这些标志,无论何时触发 KVO 机制,您都将在 observeValue(...) 中收到两个值(旧值和新值),您可以从中决定如何处理其中任何一个。但是,当 willSet 的功能几乎相同并且更方便时,为什么您需要如此复杂:

var myVariable : String! {
    willSet {
        print("Old value is: \(myVariable)")
        print("New value is: \(newValue)")

        // Let's do something with our old value
    }
    didSet {
        print("Just set the new value: \(newValue)")

        // New value is set. Let's do some actions.
    }
}

KVO 通常 每次设置观察到的 属性 时调用,即使它与上次的值相同。但我猜你正在观察 UserDefaults,它有一个特性可以防止这种情况发生(可能是一种优化,可以防止不必要的商店保存)。

您可以注册.didChangeNotification,无论值是否改变,都显示调用:

NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: .main) { notification in
    print("notification", notification)
}

如果要跟踪NSUserDefaults中的每个设置值,即使新值与之前的值相同,用NSDictionary包裹值,并在字典中放入NSUUID 值,每次作为新的 setValue 被调用时生成。

之前(observeValueForKeyPath 没有调用每个 setValue):

[self.mySharedDefaults setValue: @"CHECKING" forKey:@"appStatusOUT"];

之后(observeValueForKeyPathsetValue 被调用):

[self.mySharedDefaults setValue: [NSDictionary dictionaryWithObjectsAndKeys: @"CHECKING", @"CMD",
                          [NSUUID UUID].UUIDString, @"UUID", nil] forKey:@"appStatusOUT"];