Re: Crash in KVO when using -keyPathsForValuesAffecting<Key>
Re: Crash in KVO when using -keyPathsForValuesAffecting<Key>
- Subject: Re: Crash in KVO when using -keyPathsForValuesAffecting<Key>
- From: Jeff Johnson <email@hidden>
- Date: Sat, 20 Jun 2009 13:45:33 -0500
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 (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