On Mar 11, 2009, at 2:09 AM, Dave Keck wrote:

Also, at the time that you invoke -willChangeValueForKey: have you already
changed your internal state such that invoking [theProxy
valueForKey:@"someFeature"] returns the "after" value? It should still return the "before" value at that point. It must not return the "after"
value until after the -willChangeValueForKey: call completes.

I'm changing the value of the 'someFeature' key after willChange, and
before didChange. That is, from the context of PrefCtrl:

   // #1: value for 'someFeature' is nil at this point
[self willChangeValueForKey: @"values"];
   // #2: value for 'someFeature' is still nil at this point
[valuesDictionary setValue: someFeature forKey: @"someFeature"];
   // #3 value for 'someFeature' is NOT nil at this point
[self didChangeValueForKey: @"values"];
// #4 value for 'someFeature' is the same as point #3 (still not nil)

(Note that valuesDictionary is PrefCtrl's underlying backing store for
all the prefs.)

OK, so I poked at your example project some. First, it seems I was wrong. KVO doesn't unwind its observation bookkeeping during - willChange...; it apparently does it during -didChange....

The problem is this. You're telling KVO that the "values" property has changed. So, during -didChange... it's trying to unwind its observation of the key sub-path "someFeature.isEnabled" from the old "values" value and then set up observation of that sub-path on the new "values". The problem is that the old "values" is actually the new "values" so when KVO asks the old "values" for valueForKey:@"someFeature" it is not getting nil, it's getting the new object. It then goes to cease observing "isEnabled" on that object and finds that it never was observing that object.

The problem is that old "values" and new "values" are really the same thing, while KVO wants to work with the old and new side by side simultaneously. I'm guessing that It has saved the old "values" during the -willChange... call and then references it during the - didChange... call, expecting it to still be old.

This all makes sense if "values" were a more normal property. There would be two different objects. In your example, there's no good reason that the dictionary could not actually serve as the value of "values", although I gather that that doesn't serve your real application's needs. Still, if it were done that way, then a change of the "values" property would mean replacing that dictionary with a different one, in which case KVO could continue to reference the old one during -didChange... (presuming it retained it during - willChange...).

Am I right that just using a dictionary for the "values" property doesn't meet your needs? It would be a mutable dictionary and in many cases you'd change its member objects. However, when there was an external change of the preferences, you might just load a new dictionary and replace the old one (using a normal, KVO-compliant setter). That ought to work with KVO.

The problem with using dictionaries like this is that it violates encapsulation -- changes are made to the dictionary's contents without necessarily going through methods that you wrote. So, you don't have an opportunity to respond to the change. You can work around that by creating a simple wrapper class around a dictionary. It would have roughly the same interface as NSMutableDictionary, and forward each call to the dictionary it uses as its backing store, but you would have the opportunity to intervene in each method if necessary.


I'm not sure if it's relevant, but does it matter that someFeature is
originally nil - I'm assuming it couldn't possibly have an observer
before I change the value to something non-nil...?

No, I don't think that's the issue you're currently seeing. However, the AppKit release notes for 10.5 discussed a fix for a bug in 10.4 about observing through a relationship that was originally nil. If you plan to deploy to 10.4, you should check those notes.


Since you are using a proxy to front for your PrefCtrl, are you
also making sure that all changes of PrefCtrl's properties cause change
notifications for the proxy's virtual properties?  That is, are you
forwarding all -will/didChange... invocations on your PrefCtrl object to
your proxy, at least for keys other than "values"?

That's precisely my issue - because the prefs can be changed from
other processes, the app doesn't know which specific properties
changed - all it knows is that there was a change.

My point was about for the other cases. Surely, there are some cases when the properties of PrefCtrl are changed internally, too, right? If so, then any change of one of those properties needs to appear to KVO as a change of a property of the proxy, so you would need to forward the -will/didChange... calls.

Regards,
Ken

_______________________________________________

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

Reply via email to