KVO在iOS 9.3中被破坏了

这可能是iOS 9.3(发布)中的一个糟糕的错误。

[NSUserDefaults standardUserDefaults]添加单个观察者时,我注意到响应方法-observeValueForKeyPath:ofObject:change:context:被多次调用。

在下面的简单示例中,每次按下一次UIButton时,observeValueForKeyPath会触发两次。 在更复杂的例子中,它会发射更多次。 它仅出现在iOS 9.3上(包括sim和设备)。

这显然会对应用程序造成严重破坏。 其他人经历过同样的事情吗?

 // ViewController.m (barebones, single view app) - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"viewDidLoad"); [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"SomeKey" options:NSKeyValueObservingOptionNew context:NULL]; } - (IBAction)buttonPressed:(id)sender { NSLog(@"buttonPressed"); [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"SomeKey"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"observeValueForKeyPath: %@", keyPath); } 

是的我也遇到了这个问题,它似乎是一个错误,下面是我正在使用的快速解决方法,直到这个问题得到解决。 我希望它有所帮助!

另外要澄清的是,由于iOS 7 KVO在NSUserDefaults上工作得非常好,并且它确实看起来像Matt所说的那样具有关键的值可观察性,因此在iOS 9.3 SDK中明确地用NSUserDefaults.h编写:“可以使用Key-Value观察NSUserDefaults观察存储在其中的任何密钥。“

 #include  #include  @property uint64_t newTime; @property uint64_t previousTime; @property NSString *previousKeyPath; -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { //Workaround for possible bug in iOS 9.3 SDK that is causing observeValueForKeyPath to be called multiple times. newTime = mach_absolute_time(); NSLog(@"newTime:%llu", newTime); NSLog(@"previousTime:%llu", previousTime); //Try to avoid duplicate calls if (newTime > (previousTime + 5000000.0) || ![keyPath isEqualToString:previousKeyPath]) { if (newTime > (previousTime + 5000000.0)) { NSLog(@"newTime > previousTime"); previousTime = newTime; NSLog(@"newTime:%llu", newTime); NSLog(@"previousTime:%llu", previousTime); } if (![keyPath isEqualToString:previousKeyPath]) { NSLog(@"new keyPath:%@", keyPath); previousKeyPath = keyPath; NSLog(@"previousKeyPath is now:%@", previousKeyPath); } //Proceed with handling changes if ([keyPath isEqualToString:@“MyKey"]) { //Do something } } } 

[NSUserDefaults standardUserDefaults]添加单个观察者时,我注意到响应方法-observeValueForKeyPath:ofObject:change:context:被多次调用

这是一个已知问题,并且(在Apple) 报告已在iOS 11和macOS 10.13中修复。

为MacOS(10.13)添加这个答案肯定有错误获取NSUserDefault Keys的KVO的多个通知,并且还解决了弃用问题。 最好使用经过纳秒的计算,为您运行的机器获取它。 这样做:

 #include  #include  static mach_timebase_info_data_t _sTimebaseInfo; uint64_t _newTime, _previousTime, _elapsed, _elapsedNano, _threshold; NSString *_previousKeyPath; -(BOOL)timeThresholdForKeyPathExceeded:(NSString *)key thresholdValue:(uint64_t)threshold { _previousTime = _newTime; _newTime = mach_absolute_time(); if(_previousTime > 0) { _elapsed = _newTime - _previousTime; _elapsedNano = _elapsed * _sTimebaseInfo.numer / _sTimebaseInfo.denom; } if(_elapsedNano > threshold || ![key isEqualToString:_previousKeyPath]) { if(![key isEqualToString:_previousKeyPath]) _previousKeyPath = key; return YES; } return NO; } } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if(![self timeThresholdForKeyPathExceeded:keyPath thresholdValue:5000000]) return; // Delete this line of MacOS bug ever fixed } // Else this is the KeyPath you are looking for Obi Wan, process it. } 

这基于此Apple Doc的清单2: https : //developer.apple.com/library/content/qa/qa1398/_index.html

Interesting Posts