简体   繁体   中英

cocoa - Subtle difference between -removeObserver:forKeyPath: and -removeObserver:forKeyPath:context:?

Short version:

What use is -removeObserver:forKeyPath: ?

Why not always use -removeObserver:forKeyPath:context: ?

Long version

While working on a Cocoa program, I discovered that using -removeObserver:forKeyPath: could (but would not always) lead to an error like:

Cannot remove an observer <ObservedClass 0x1001301d0> for the key path "exampleKeyPath" from <__NSCFConstantString 0x100009138> because it is not registered as an observer.

while using -removeObserver:forKeyPath:context: instead would work just fine.

Since it is required that a context be specified when setting up observation (with -observeValueForKeyPath:ofObject:change:context: ), I'm puzzled at why the context: -less removal method exists.

Based on my reading of the NSKeyValueObserving Protocol , I supposed that the removal might apply to the specified observer and specified key path in all contexts, but the failure of -removeObserver:forKeyPath: (with no context) to work as a replacement for -removeObserver:forKeyPath:context: (with a context of NULL ) seems to shoot down that idea.

So: why might I have that error? What does -removeObserver:forKeyPath: do with contexts? How's it differ from its context: -equipped younger sibling?

Code example

Problematic code:

-(void) invalidate {
    [(id)observedObject removeObserver:self
                            forKeyPath:@"exampleKeyPath"];
}

Non-Problematic code:

-(void) invalidate {
    [(id)observedObject removeObserver:self
                            forKeyPath:@"exampleKeyPath"
                            context:NULL];
}

Short version: -removeObserver:forKeyPath:context: was only introduced in 10.7, hence both.

Long version: Why might you have the error? Looks like a bug, either in your code or the system (I've never seen the error and use the shorter version a lot). The descriptions of the two methods do not suggest there should be any difference. If nobody else comes up with an explanation, and you can't find anything in your code, then report a bug to Apple.

The documentation has an excellent discussion as to the use of the new method:

Examining the value in context you are able to determine precisely which addObserver:forKeyPath:options:context: invocation was used to create the observation relationship. When the same observer is registered for the same key-path multiple times, but with different context pointers, an application can determine specifically which object to stop observing

It's just a way to be more specific about just which binding you want to remove from the object. For example, I might bind to a keypath twice, but with the memory locations of different static variables, a little like how dispatch_once() works. The context-free subscription method was the only way of binding to an object until 10.7 rolled around and filled in that gap.

As for your KVO troubles, the problem can occur in many different cases. The most common being that you've subscribed on one thread, then very soon after, removed a subscription from a different thread. Occasionally it can occur because the object you tried to observe was just about to deallocate, meaning you would subscribe to some bogus memory location that happened to fill what you needed to, then removing the subscription from this garbage pointer would be impossible. Either way, make sure to monitor the methods you're utilizing bindings in, as they can be a little unstable if used in the wrong way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM