On Oct 15, 2008, at 4:53 AM, Sebastian Morawietz wrote:

A Bridge object acts as a drop in for a Person-Object, with an
NSString* property called name and an Address* property address.
Binding to the keyPath "name" or "address" of the Bridge works nicely.
Trouble starts when binding some object to the keyPath
"address.street" of the bridge and a new Address-Object is set for
Person's address property. That results in KVO-related exceptions that
look like this:

* Cannot remove an observer <NSKeyValueObservance 0x126b00> for the
key path "street"
* from <Address 0x12f1d0> because it is not registered as an observer

On which line of testBindingToStreet() does the exception occur? What is the stack trace?

I assume the Address object being mentioned in the exception is the new one created in -[Person modifyAddress]. Have you confirmed that?

One of the problems I noticed is in -[Bridge observeValueForKeyPath:...]. You are treating a key path as though it were a key. will/didChangeValueForKey: don't take a key path; they treat whatever is passed as a key, even if it contains a period. (Note that there can be keys with embedded periods, like if a dictionary has keys like "com.example.foo" or "John Q. Public".)

More seriously, observeValueForKeyPath:... doesn't call through to super. Your use of KVB is implicitly using KVO, plus you're also explicitly using KVO (from valueForUndefinedKey:). Your observeValueForKeyPath:... only handles the latter. Therefore, you're preventing the other observing code from coping with change notifications.

On top of that, I think there's a fundamental mismatch between will/ didChangeValueForKey: and observeValueForKeyPath:... that you can't account for. It should be the case that willChangeValueForKey: is sent before the value changes and didChangeValueForKey: is sent afterward. But observeValueForKeyPath:... is only invoked by KVO after both of those are sent. You are turning around and trying to use will/didChangeValueForKey: within your observeValueForKeyPath:... You are pretending that the change is occurring during observeValueForKeyPath:... when it's really happened already.

KVO is trying to switch of which "address" it's watching the "street" property. But it's being told to make this switch after the change has already occurred in the "obj" which Bridge holds. Bridge's observeValueForKeyPath:... is invoked _after_ obj's address is done changing. But during your explicit invocation of willChangeValueForKey:, KVO dutifully asks Bridge for the value of its "address" property in order to find out from which "address" it should unregister interest in the "street" property. Bridge asks its "obj" and gets the new Address, not the old one.

In order to do what you're trying to do, you'd need some way to hook more directly into the will/didChangeValueForKey: machinery. I don't know if it's possible. Alternatively, Bridge will have to cache its own copy of each property of 'obj'. It will be out of sync briefly with obj because Bridge will only be able to update its copy during observeValueForKeyPath:..., but this is actually exactly what you need to fix the problem. You would issue the willChangeValueForKey: call before updating Bridge's copy, update the copy, then invoke didChangeValueForKey: afterward. KVO would then have access to the old address so it can unregister itself properly.

This is a hard problem. Also, I'm not really sure why you're attempting to do what you're doing. The dynamism of KVC/KVO should allow you to achieve your ends without the Bridge pattern. After all, your bridge implementation requires that the wrapped object is already KVC/KVO-compliant for the keys being accessed. So, I'm not sure what you're hoping to gain. Can you explain what you need this for?


As an aside, here are a couple of nit-picks about the code:

* On 10.5 and later, you can use -forwardingTargetForSelector: for simpler and faster forwarding. It's only documented (so far) in the Leopard release notes <http://developer.apple.com/releasenotes/Cocoa/Foundation.html >.

* Bridge's observedKeys should probably be a NSMutableSet rather than an NSMutableDictionary, since you only care about the presence or absence of keys and never about values.


Cheers,
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 [EMAIL PROTECTED]

Reply via email to