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<NSString *,id> *)change context:(void *)context { NSLog(@"observeValueForKeyPath: %@", keyPath); } 

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

另外,为了澄清,由于iOS 7 KVO一直在NSUserDefaults上工作良好,它看起来像是Matt所说的关键值,它明确地写在iOS 9.3 SDK的NSUserDefaults.h中:“NSUserDefaults可以使用Key-Value观察存储在其中的任何密钥。“

 #include <mach/mach.h> #include <mach/mach_time.h> @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:被多次调用

这是一个已知的问题,并报告 (由苹果)在iOS 11和macOS 10.13固定。

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

 #include <mach/mach.h> #include <mach/mach_time.h> 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