这可能是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);
}
答案 0 :(得分:5)
是的我也遇到了这个问题而且似乎是一个错误,下面是我正在使用的快速解决方法,直到修复完毕。我希望它有所帮助!
另外要澄清的是,由于iOS 7 KVO在NSUserDefaults上运行良好,并且它看起来像Matt所说的那样具有关键的值可观察性,因此它在iOS 9.3 SDK中的NSUserDefaults.h中明确写出:“可以使用NSUserDefaults进行观察键值观察存储在其中的任何键。“
#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
}
}
}
答案 1 :(得分:2)
向
[NSUserDefaults standardUserDefaults]
添加单个观察者时,我注意到多次调用了响应方法-observeValueForKeyPath:ofObject:change:context:
这是一个已知问题,在iOS 11和macOS 10.13中已修复reported(Apple)。
答案 2 :(得分:0)
为MacOS(10.13)添加此答案,肯定有错误获取NSUserDefault Keys的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