Screwy Binding/KVO
Hi, Sorry if this has been posted before; I did a quick scour but might not have used the right keywords (this issue isn't very general). Basically I'm noticing that when I expect KVO observations to be triggered, they aren't. Most likely this is due to my misunderstanding, but I'd like to post a test case here for good measure (I write this last night so bear with me if the comments are a little strange). #import Foundation/Foundation.h #import AppKit/AppKit.h @interface SDObject : NSObject { NSArray *content; } @property (readwrite, retain) NSArray *content; @end int main (int argc, const char * argv[]) { [NSAutoreleasePool new]; SDObject *object = [[SDObject alloc] init]; NSArrayController *controller1 = [[NSArrayController alloc] init]; NSArrayController *controller2 = [[NSArrayController alloc] init]; NSLog(@mark 1); // when this is called, the KVO in SDObject is triggered with an empty array [object bind:@content toObject:controller2 withKeyPath:@arrangedObjects options:nil]; NSLog(@mark 2); [controller2 bind:@contentArray toObject:controller1 withKeyPath:@arrangedObjects options:nil]; NSLog(@mark 3); // these might suppose to trigger it (i thought it would but it doesnt) [controller1 addObject:@test1]; [controller1 addObject:@test2]; // okay i *know* this and the -didChange... should trigger it [controller2 willChangeValueForKey:@arrangedObjects]; // this should too though, right? [controller2 rearrangeObjects]; // (see -willChange... above) [controller2 didChangeValueForKey:@arrangedObjects]; // proving that they all contain the right things NSLog(@%@, [controller2 arrangedObjects]); NSLog(@%@, [object content]); return EXIT_SUCCESS; } @implementation SDObject @synthesize content; + (void) initialize { if (self == [SDObject class]) { [self exposeBinding:@content]; } } - (id) init { self = [super init]; [self addObserver:self forKeyPath:@content options:0 context:NULL]; return self; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@observing keypath [...@] == [...@], keyPath, [self valueForKeyPath :keyPath]); } @end This is the output: *2009-12-11 11:10:02.139 ScrewyKVO[21311:a0f] mark 1* *2009-12-11 11:10:02.515 ScrewyKVO[21311:a0f] observing keypath [content] == [(* *)]* *2009-12-11 11:10:02.516 ScrewyKVO[21311:a0f] mark 2* *2009-12-11 11:10:02.521 ScrewyKVO[21311:a0f] mark 3* *2009-12-11 11:10:02.522 ScrewyKVO[21311:a0f] (* *test1,* *test2* *)* *2009-12-11 11:10:02.522 ScrewyKVO[21311:a0f] (* *test1,* *test2* *)* * * -- Steven Degutis http://www.thoughtfultree.com/ http://www.degutis.org/ ___ 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
Re: Screwy Binding/KVO
On Dec 11, 2009, at 09:10, Steven Degutis wrote: [reformatted to be less annoying] // when this is called, the KVO in SDObject is triggered with an empty array [object bind:@content toObject:controller2 withKeyPath:@arrangedObjects options:nil]; NSLog(@mark 2); Yeah, well, at this point controller2 has no content array, so of course its 'arrangedObjects' is empty. However, this line of code is the heart of your problem, because SDObject has no content binding. It has a content property, but that's not the same as a binding. Compare this with (say) a NSTableView, which has a content binding but no content property. 'bind:toObject:withKeyPath:options:' cannot be used as you are doing, in spite of what the documentation might suggest for the binding parameter: The key path for a property of the receiver previously exposed using the exposeBinding: method. It's not a key path but a binding name. Look at: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html#//apple_ref/doc/uid/20002373 if you want gory details, but even that document is somewhat inexact in its terminology. It sometimes confuses properties and binding names, or it sometimes uses binds from ... to in the opposite direction from the way we usually say it, or it sometimes confuses the idea that bindings are two-way (they are) with the idea that they're symmetrical (they are not) -- it's a bit hard to tell which. [controller2 bind:@contentArray toObject:controller1 withKeyPath:@arrangedObjects options:nil]; NSLog(@mark 3); // these might suppose to trigger it (i thought it would but it doesnt) [controller1 addObject:@test1]; [controller1 addObject:@test2]; In your sample code, you never gave controller1 any content array. There's nothing to add objects to. // okay i *know* this and the -didChange... should trigger it [controller2 willChangeValueForKey:@arrangedObjects]; Sending willChange/didChange to an object whose implementation you don't control seems like a *terrible* idea. Especially something funky like NSArrayController. // this should too though, right? [controller2 rearrangeObjects]; // (see -willChange... above) [controller2 didChangeValueForKey:@arrangedObjects]; I'm actually surprised you didn't get any exceptions using this code. Did you check the log? ___ 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