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

Posted

技术标签:

【中文标题】iOS KVO - 检测何时再次设置相同的值【英文标题】:iOS KVO - detect when the same value is set again 【发布时间】:2017-10-22 12:21:35 【问题描述】:

是否可以使用 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)

【问题讨论】:

无关,如果你打算使用addObserver,你真的应该使用context参数来区分你的观察者和超类可能使用的任何观察者。或者使用新的 Swift 4 KVO 语法来消除这个问题。 【参考方案1】:

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

您可以注册.didChangeNotification,无论值是否改变,它似乎都会调用:

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

【讨论】:

但是我如何检测到设置了特定键?我只需要监控 1 个键。 使用该通知时,我认为没有办法知道设置了哪个。您可能需要考虑 UserDefaults 以外的其他内容。或者将其包装在可以触发您需要观察的东西中。【参考方案2】:

你可以这样做:

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

【讨论】:

【参考方案3】:

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.
    

【讨论】:

【参考方案4】:

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

之前(observeValueForKeyPath 不是每个setValue 都调用过):

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

之后(observeValueForKeyPath 被每个 setValue 调用):

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

【讨论】:

以上是关于iOS KVO - 检测何时再次设置相同的值的主要内容,如果未能解决你的问题,请参考以下文章

IOS、UIView、检测子视图隐藏状态变化

Angular 2 - 检测绑定属性何时收到值

[iOS]:检测从另一个外部应用程序返回后视图控制器何时出现

iOS - 拖放碰撞检测如何检测您选择的项目何时拖过另一个子视图?

iOS:检测我的 UIView 何时添加到其他视图中

Android:检测 ScrollView 何时停止滚动