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