KVO on iOS 9.3

This could be a terrible bug in iOS 9.3 (release).

When adding one observer to [NSUserDefaults standardUserDefaults] I noticed that the response method -observeValueForKeyPath:ofObject:change:context: is called several times.

In the simple example below, every time a UIButton is clicked once, watchValueForKeyPath fires twice. In more complex examples, it fires even more times. It is present only on iOS 9.3 (both on sim and on devices).

This can lead to chaos in the application. Does anyone else experience the same thing?

 // 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); } 
+6
source share
3 answers

Yes, I also experience this, and it seems to be a mistake, below is a short workaround that I am currently using until it is fixed. Hope this helps!

Also, to clarify, since iOS 7 KVO works great with NSUserDefaults, and this is undoubtedly the key value observed, as Matt said, it is explicitly written in the NSUserDefaults.h in the iOS 9.3 SDK: "NSUserDefaults can be observed using the Key Note value for any key stored in it. "

 #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 } } } 
+4
source

When adding one observer to [NSUserDefaults standardUserDefaults] I noticed that the response method -observeValueForKeyPath:ofObject:change:context: is called several times

This is a known issue and reported (from Apple) as being fixed in iOS 11 and macOS 10.13.

+2
source

Adding this answer for MacOS (10.13), which definitely has the error of getting multiple notifications for KVO from NSUserDefault Keys, as well as fixing the flaws. It is better to use the calculation for the past nano seconds, which gets it for the machine on which you are working. Do it like this:

 #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. } 

This is based on Listing 2 of this Apple Doc : https://developer.apple.com/library/content/qa/qa1398/_index.html

0
source

All Articles