Screwy Binding/KVO

2009-12-11 Thread Steven Degutis
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

2009-12-11 Thread Quincey Morris
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