Bug+Fix: NSController (and subclasses) have problems with custom KVO keys
Bug+Fix: NSController (and subclasses) have problems with custom KVO keys
- Subject: Bug+Fix: NSController (and subclasses) have problems with custom KVO keys
- From: email@hidden
- Date: Thu, 19 Mar 2009 16:53:25 -0000 (GMT)
- Importance: Normal
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 (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