Hi, I want to report a bug (will file an official bug at bugreporter.apple.com in a second but wanted to share this with the list to see if any one else saw this. I included a simple sample code to reproduce it, this sample code also contains a fix.
Bug in short Terms: KVO or Bindings with a custom key like "targetPhoto" here in my example doesn't work properly when that keyPath contains an extra element like "targetPhoto.name" and when the object is an NSController or (subclass because I notice this bug with NSArrayController). You will also see an exception when you removeObserver for that key after you first set the key. This sequence basically causes an exception on the removeObserver call. [myArraController addObserver: ... keyPath "targetPhoto.name" ...]; [myArraController setTargetPhoto:...]; // this Triggers the KVO notification [myArraController removeObserver: ... keyPath "targetPhoto.name"]; This was my initial bug that I was tracing [myArraController addObserver: ... keyPath "targetPhoto.name" ...]; [myArraController setTargetPhoto:...]; // this Triggers the KVO notification [[myArraController targetPhoto:] setName:@"the new name"]; // this DOES NOT Trigger the KVO notification [myArraController removeObserver: ... keyPath "targetPhoto.name"]; By the way this above code works fine in case the key is "arrangedObjects" and do something like this: [myArraController addObserver: ... keyPath "arrangedObjects.name" ...]; [myArraController setTargetPhoto:...]; // this Triggers the KVO notification [[myArraController targetPhoto:] setName:@"the new name"]; / this Triggers the KVO notification [myArraController removeObserver: ... keyPath "arrangedObjects.name"]; This was tested on MacOS 10.5.6 Solution: The bug seems to be in NSController Code willChangeValueForKey and didChangeValueForKey, so for my custom keys I'm calling directly the NSObject code, because this is some like [super super super .... There is a little hack involved to get this to work... So uncomment that code and you will see that everything works fine then... regards Marc Van Olmen // // main.m // TestArrayController #import <Cocoa/Cocoa.h> #import <objc/message.h> static char * kObservationContext_PhotoArrayControllerTargetPhotoPhotoTransoformationSettingsFilmType = (char*)33;// "PhotoArrayController.TargetPhoto.PhotoTransformationSetting.Filmtype"; @interface NSMySimpleObject : NSObject { NSString * mName; } - (void)setName:(NSString*)theName; - (NSString*)name; @end @implementation NSMySimpleObject - (NSString*)name; { return mName; } - (void)setName:(NSString*)theName; { [theName retain]; [mName release]; mName = theName; } @end @interface NSMyArrayController : NSArrayController { NSMySimpleObject* mTargetPhoto; } - (void)setTargetPhoto:(NSMySimpleObject*)theTargetPhoto; - (NSMySimpleObject*)targetPhoto; @end @implementation NSMyArrayController /* - (void)willChangeValueForKey:(NSString *)theKey; { if ([theKey hasPrefix:@"targetPhoto"]) { NSLog(@"NSMyArrayController willChangeValueForKey: %@",theKey); struct objc_super superduper= { .receiver=self, .class=[NSObject class] }; objc_msgSendSuper(&superduper,_cmd,theKey); } else { [super willChangeValueForKey:theKey]; } } - (void)didChangeValueForKey:(NSString *)theKey; { if ([theKey hasPrefix:@"targetPhoto"]) { NSLog(@"NSMyArrayController didChangeValueForKey: %@",theKey); struct objc_super superduper= { .receiver=self, .class=[NSObject class] }; objc_msgSendSuper(&superduper,_cmd,theKey); } else { [super didChangeValueForKey:theKey]; } } */ -(NSMySimpleObject*)targetPhoto; { return mTargetPhoto; } -(void)setTargetPhoto:(NSMySimpleObject*)thePhoto; { [thePhoto retain]; [mTargetPhoto release]; mTargetPhoto = thePhoto; } @end @interface Observer : NSObject{} @end @implementation Observer -(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"observed!"); } @end int main(int argc, char *argv[]) { NSAutoreleasePool *aPool=[[NSAutoreleasePool alloc] init]; NSMyArrayController *myTestArrayController = [[NSMyArrayController alloc] init];//initWithContent:[NSArray arrayWithArray:[wPhotoArrayController content]]]; Observer *o=[[Observer alloc] init]; [myTestArrayController addObserver:o forKeyPath:@"targetPhoto.name" options:NSKeyValueObservingOptionNew context:kObservationContext_PhotoArrayControllerTargetPhotoPhotoTransoformationSettingsFilmType]; NSMySimpleObject * anObject =[[NSMySimpleObject alloc] init]; [anObject setName:@"test"]; [myTestArrayController setValue:anObject forKey:@"targetPhoto"]; id p = [myTestArrayController valueForKey:@"targetPhoto"]; [p setValue:@"spammo" forKey:@"name"]; [myTestArrayController removeObserver:o forKeyPath:@"targetPhoto.name"]; [aPool release]; } _______________________________________________ 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