Re: Implementing a KVO/Bindings-Compliant Bridge-Pattern in Cocoa
Re: Implementing a KVO/Bindings-Compliant Bridge-Pattern in Cocoa
- Subject: Re: Implementing a KVO/Bindings-Compliant Bridge-Pattern in Cocoa
- From: Ron Lue-Sang <email@hidden>
- Date: Fri, 17 Oct 2008 19:35:42 -0700
On Oct 15, 2008, at 2:53 AM, Sebastian Morawietz wrote:
Hi folks,
I'm trying to implement a simple object bridge in cocoa where the
bridge object acts as a kvo/bindings-compliant drop in for some
arbitrary other NSObject-instance.
Here is my problem (more details in the attached code):
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
This happens even though the bridge notices the change in the
"address"-Property and emits a
willChangeValueForKeyPath/didChangeValueForKeyPath tuple.
The attached code produces the the problem. The file contains a
main-function can be compiled and run with
* gcc -o test BridgeDemo.m -framework AppKit -framework
Foundation; ./test
If you know a solution to this problem or can offer me a better
approach solving the same problem you make me a very happy programmer.
Don't do this. You're lying by doing this. Kittens die when you do this.
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context
{
NSLog( @">>>> Detected Change in keyPath: %@", keyPath );
[self willChangeValueForKey:keyPath];
[self didChangeValueForKey:keyPath];
}
Alternatives include:
1) Registering for the "prior" notification by passing
NSKeyValueObservingOptionPrior instead of using "new" as an option
when registering as an observer. Then you can write your
observeValueForKeyPath method like
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context
{
// NSLog( @">>>> Detected Change in keyPath: %@", keyPath );
if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey]
boolValue] == YES) {
[self willChangeValueForKey:keyPath];
} else {
[self didChangeValueForKey:keyPath];
}
}
2) Cache the old value in the object and don't use any of the KVO
options. This is the one I like the most.
static NSString *NoValueMarker = @"NoValueMarker";
static NSString *NilValueMarker = @"NilValueMarker";
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context
{
// NSLog( @">>>> Detected Change in keyPath: %@", keyPath );
[self willChangeValueForKey:keyPath];
[observedKeys setObject:NoValueMarker forKey:keyPath];
[self didChangeValueForKey:keyPath];
}
-(id)valueForUndefinedKey:(NSString*)key {
id result = [observedKeys objectForKey:key];
if (result == nil) {
[obj addObserver:self forKeyPath:key options:0 context:nil];
}
if ((result == nil) || (result == NoValueMarker)) {
result = [obj valueForKey:key];
if (result != nil) {
[observedKeys setObject:result forKey:key];
} else {
[observedKeys setObject:NilValueMarker forKey:key];
}
}
if (result == NilValueMarker) {
result = nil;
}
return result;
}
--------------------------
RONZILLA
_______________________________________________
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