Re: KVO & Bindings: Proxy object & change notifications
Re: KVO & Bindings: Proxy object & change notifications
- Subject: Re: KVO & Bindings: Proxy object & change notifications
- From: Ken Thomases <email@hidden>
- Date: Wed, 11 Mar 2009 03:25:35 -0500
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 (email@hidden)
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:
This email sent to email@hidden