One of the main issues with KVO is caused by classes not knowing what observers a superclass has registered.
When a subclass also wants to observe the same keyPath
they have the danger of removing the observer from the super class.
- (void)someMethod
{
[self addObserver:self
forKeyPath:@"view.frame"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"view.frame"])
{
[self viewDidChangeFrame];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)removeKVO
{
[self removeObserver:self forKeyPath:@"view.frame"];
}
When super
also expects to receive callbacks for view.frame
expected behaviour will quickly break and be hard to track down.
To protect against this the context
can be used to distinguish between different classes:
static void *HCViewControllerKVOContext = &HCViewControllerKVOContext;
- (void)someMethod
{
[self addObserver:self
forKeyPath:@"view.frame"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HCViewControllerKVOContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == HCViewControllerKVOContext)
{
if ([keyPath isEqualToString:@"view.frame"])
{
[self viewDidChangeFrame];
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)remoevKVO
{
[self removeObserver:self forKeyPath:@"view.frame" context:HCViewControllerKVOContext];
}
This will also eliminate calling -[super observeValueForKeyPath:ofObject:change:context:]
when the super class doesn't expect it, which will crash.