In my program, I use KVO manually to observe changes in object property values. I get an EXC_BAD_ACCESS signal in the following line of code inside a custom setter:
[self willChangeValueForKey:@"mykey"];
The strange thing is that this happens when the factory method calls the custom setter, and there shouldn't be any observers around it. I do not know how to debug this situation.
Update:. A list of all registered observers is observationInfo . It turned out that the object was actually specified, pointing to an invalid address. However, I do not know at all how he got there.
Update 2: Apparently, the same callback of the object and method can be registered several times for this object, which leads to identical entries in the observed object observationInfo . When you unregister, only one of these entries is deleted. This behavior is a little contrary to intuition (and it is certainly a mistake in my program to add several entries at all), but it does not explain how false observers can mysteriously appear in freshly allocated objects (unless caching / reuse exists that I donβt I know).
Modified question: How can I find out WHERE and WHEN an object is registered as an observer?
Update 3: Specific code example.
ContentObj is a class that has a dictionary as a property called mykey . It redefines:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { BOOL automatic = NO; if ([theKey isEqualToString:@"mykey"]) { automatic = NO; } else { automatic=[super automaticallyNotifiesObserversForKey:theKey]; } return automatic; }
A pair of properties has getters and setters as follows:
- (CGFloat)value { return [[[self mykey] objectForKey:@"value"] floatValue]; } - (void)setValue:(CGFloat)aValue { [self willChangeValueForKey:@"mykey"]; [[self mykey] setObject:[NSNumber numberWithFloat:aValue] forKey:@"value"]; [self didChangeValueForKey:@"mykey"]; }
The container class has the contents property of the NSMutableArray class, which contains instances of the ContentObj class. It has several methods that manually process the registration:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { BOOL automatic = NO; if ([theKey isEqualToString:@"contents"]) { automatic = NO; } else { automatic=[super automaticallyNotifiesObserversForKey:theKey]; } return automatic; } - (void)observeContent:(ContentObj *)cObj { [cObj addObserver:self forKeyPath:@"mykey" options:0 context:NULL]; } - (void)removeObserveContent:(ContentObj *)cObj { [cObj removeObserver:self forKeyPath:@"mykey"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (([keyPath isEqualToString:@"mykey"]) && ([object isKindOfClass:[ContentObj class]])) { [self willChangeValueForKey:@"contents"]; [self didChangeValueForKey:@"contents"]; } }
There are several methods in a container class that modify contents . They look like this:
- (void)addContent:(ContentObj *)cObj { [self willChangeValueForKey:@"contents"]; [self observeDatum:cObj]; [[self contents] addObject:cObj]; [self didChangeValueForKey:@"contents"]; }
And a couple more others that provide similar functionality to the array. They all work by adding / removing themselves as observers. Obviously, everything that leads to numerous registrations is a mistake and may contain somewhere hidden in these methods.
My question discusses strategies for debugging this kind of situation. As an alternative, please feel free to suggest an alternative strategy for implementing this type of notification / observer template.
Update 4:. I found a bug using a mixture of breakpoints, NSLog s, code reviews and sweating. I did not use context in KVO, although this is certainly another useful suggestion. This was an incorrect double registration, which for reasons beyond my understanding led to the observed behavior.
An implementation involving [self willChange...]; [self didChange...] [self willChange...]; [self didChange...] , works as described (on iOS 5), although it is far from beautiful. The problem is that since NSArray not KVO-compatible, it is impossible to talk about changes in its contents. I also thought about the notifications offered by Mike Ash, but I decided to go with KVO, as it looked like a more Cocoa like mechanism for working. This may not be the best solution ...