When you implement + (NSSet *)keyPathsForValuesAffectingName { return [NSSet setWithObject:@"object.name"]; }, you're implicitly setting up KVO, so whenever the name of the MyObject instance changes, KVO notifications are sent for the name of the MyWrapper instance. When you manually call addObserver: and removeObserver: for exactly the same instances and keypaths, it's redundant and probably messes with the automatic KVO.

-Jeff


On Jun 20, 2009, at 1:08 PM, Jean-Daniel Dupas wrote:

Hello,

I'm experiencing some difficulties with KVO and auto dependent keys. Am I doing something wrong, or should I fill a bug report ?

Here is a test case.

MyObject is a class with a single property: name.
MyWrapper is a class that wrap a MyObject instance. It also declares a property 'name' which is defined as dependent of "object.name" (using +keyPathsForValuesAffectingName).

I'm creating 2 distinct wrappers that use the same underlying object.

I start to observe both wrapper's name.

Now, I want to destroy the first wrapper. I unregister observer and release it. Then, when I change the underlying object's name, the KVO machinery try to notify the released object instead of notifying the registred one (Note that NSZombieEnabled is set to YES).

2009-06-20 20:02:35.437 kvo[20984:807] wrapper 1: <MyWrapper: 0x104f10> 2009-06-20 20:02:35.439 kvo[20984:807] wrapper 2: <MyWrapper: 0x104f90> 2009-06-20 20:02:35.440 kvo[20984:807] add observer <Foo: 0x105f60> to wrapper <MyWrapper: 0x104f10> 2009-06-20 20:02:35.442 kvo[20984:807] add observer <Foo: 0x105f60> to wrapper <MyWrapper: 0x104f90> 2009-06-20 20:02:35.443 kvo[20984:807] remove observer <Foo: 0x105f60> to wrapper <MyWrapper: 0x104f10> 2009-06-20 20:02:35.444 kvo[20984:807] *** -[MyWrapper willChangeValueForKey:]: message sent to deallocated instance 0x104f10

----------- kvo-test.m ----------

#import <Foundation/Foundation.h>
#import <Foundation/NSDebug.h>

// Simple Object Class with a simple property
@interface MyObject : NSObject {
@private
 NSString *_name;
}

@property(copy) NSString *name;

@end

@implementation MyObject

@synthesize name = _name;

- (void)dealloc {
 [_name release];
 [super dealloc];
}

@end

// Simple obejct wrapper with a name property that forward the underlyng object name
@interface MyWrapper : NSObject {
@private
 MyObject *_object;
}

@property(copy) NSString *name;
@property(retain) MyObject *object;

@end

@implementation MyWrapper

@synthesize object = _object;

- (void)dealloc {
 [_object release];
 [super dealloc];
}

- (NSString *)name { return [_object name]; }
- (void)setName:(NSString *)aName { [_object setName:aName]; }

+ (NSSet *)keyPathsForValuesAffectingName { return [NSSet setWithObject:@"object.name"]; }

@end

// Observer class
@interface Foo : NSObject { }

@end

@implementation Foo

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject: (id)object change:(NSDictionary *)change context:(void *)context {
 if (context == [Foo class]) {
   NSLog(@"name did change");
 } else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
 }
}

@end


int main(int argc, char *argv[]) {
 NSZombieEnabled = YES;

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 MyObject *obj = [[MyObject alloc] init];
 obj.name = @"Hello KVO";

 MyWrapper *w1 = [[MyWrapper alloc] init];
 NSLog(@"wrapper 1: %@", w1);
 w1.object = obj;

 MyWrapper *w2 = [[MyWrapper alloc] init];
 NSLog(@"wrapper 2: %@", w2);
 w2.object = obj;

 Foo *foo = [[Foo alloc] init];
 NSLog(@"add observer %@ to wrapper %@", foo, w1);
[w1 addObserver:foo forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:[Foo class]];

 NSLog(@"add observer %@ to wrapper %@", foo, w2);
[w2 addObserver:foo forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:[Foo class]];

 NSLog(@"remove observer %@ to wrapper %@", foo, w1);
 [w1 removeObserver:foo forKeyPath:@"name"];
 [w1 release];

 obj.name = @"Youpi";

 [pool drain];
 return 0;
}

_______________________________________________

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

Reply via email to