Alex Stone Alex Stone - 2 days ago 4
iOS Question

iOS exc_bad_access when observing NSUserDefaults with KVO and delayedSelector

I've encountered a weird bug and would like to check if I'm using my Key value observing of changes to NSUserDefaults correctly.

I have used this code in two places in my app without issues, then I added 3rd controller that observes values for "goldCount" and "energyCount". Now when I set the initial value, the app crashes with exc_bad_access. I'm adding this controller to the view 2 seconds after it's parent view appears using

performSelectorAfterDelay
.

Just before displaying the game screen, I set these properties:

//crash on this line
[[NSUserDefaults standardUserDefaults] setInteger:200 forKey: goldCount];

[[NSUserDefaults standardUserDefaults] setInteger:150 forKey: energyCount];


Within 3 different view controllers, I have this code in viewDidLoad:

NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self
forKeyPath:@"goldCount"
options:NSKeyValueObservingOptionNew
context:NULL];

[defaults addObserver:self
forKeyPath:@"energyCount"
options:NSKeyValueObservingOptionNew
context:NULL];

self.goldLabel.text = [NSString stringWithFormat:@"%i",[[GameDataManager sharedInstance] currentGoldCount]];
self.energyLabel.text = [NSString stringWithFormat:@"%i",[[GameDataManager sharedInstance] currentEnergyCount]];


Here's how the class updates it's labels:

// KVO handler
-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
change:(NSDictionary *)aChange context:(void *)aContext
{

//aKeyPath gives us the name of a user default that has changed
if([aKeyPath isEqualToString:@"goldCount"])
{
//we are interested in the new value
self.goldLabel.text = [NSString stringWithFormat:@"%i",[[aChange objectForKey:@"new"] intValue]];
}else if([aKeyPath isEqualToString:@"energyCount"])
{
self.energyLabel.text = [NSString stringWithFormat:@"%i",[[aChange objectForKey:@"new"] intValue]];
}

}


After adding a call to [[NSUserDefaults standardUserDefaults] synchronize]; I get this exception the second time around:


Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '(
): An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: goldCount
Observed object:
Change: {
kind = 1;
new = 205;
}
Context: 0x0'

Answer

NSUserDefaults is not documented to be KVO compliant so it's not possible to observe defaults by their key. This might be the reason for the crash but without a stack trace it's not possible to tell.

There is a notification you can register for that announces changes to the defaults system: NSUserDefaultsDidChangeNotification.

Comments