John Chang wrote:
Hi all, Question: is it unsafe for some reason to be adding yourself as a KVO observer during -init? We have a singleton with an -init that looks something like this: - (id)init { if ((self = [super init])) { _foo = [[NSMutableDictionary alloc] init]; _bar = [[NSMutableDictionary alloc] init]; [[XYZManager sharedManager] addObserver:self forKeyPath:@"allObjects" options:NSKeyValueObservingOptionInitial context:NULL]; } return self; } This code is running on iPhone OS. On some devices (we haven't been to narrow this down), the last line of code is throwing an exception: Sun Sep 6 13:41:26 unknown MyApp[1609] <Error>: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFDictionary setObject:forKey:]: attempt to insert nil key' We know that [XYZManager sharedManager] can't be nil, since otherwise -addObserver: would not be getting called, and the first line "if ((self = [super init]))" ensures that self can't be nil. We're pretty sure that [XYZManager sharedManager].allObjects is not nil, but even if it were nil, that shouldn't cause an exception. -allObjects is a synthesized getter with no dependent keys. No other threads are involved here. So we're stumped as to why KVO is throwing an exception. The only theory is that the NSKeyValueObservingOptionInitial option is causing KVO to do something with self immediately, but self isn't fully "set up" yet. Though it's unclear why in this particular usage that would be unsafe, or why it would make any difference. Can anyone provide any insight?
I don't see anything wrong with what you're attempting to do, as long as you're a little careful.
Your NSKeyValueObservingOptionInitial isn't being magically intercepted, I'm quite sure. However that will cause your KVO handler to be executed immediately to give you the initial value, before the registration method returns, are you fully set-up to receive it at that point, is it really the last line before you return self? What happens if your class has been subclassed and that subclass has its own observeValueForKeyPath:ofObject:change:context: method, I'm not entirely sure which one would be called, but I think it would be your subclasses, and your subclass initializer hasn't yet been called.
One thing to note, you're using a context of NULL, which I used to do all the time until I got badly bitten. Don't do that, set a context for your observations and check it, the pattern is in the documentation and XCode will even generate a stub for it. You must check the context, you must call the superclass implementation. That would save you by the way in the case you have been subclassed and the subclass method has been called as the contexts wouldn't match and the subclass would pass the observation back up to you.
_______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com