Long Story Short
Take NSObject category from here and use it like this:
if ([observable tdw_hasObserver:observer forKeyPath:@"key.path" context:nil error:nil]) {
[observable removeObserver:observer forKeyPath:@"key.path"];
} else {
// go on your merry way
}
Public API approach
For many Foundation classes, which don't have any other observers but yours, you can just check observationInfo property value. The property returns a null pointer when the object doesn't have any observers or an opaque void * pointer otherwise:
if (observable.observationInfo) {
[observable removeObserver:observer forKeyPath:@"key.path"];
} else {
// go on your merry way
}
This, however will not work for most of UIKit classes and scenarios where you yourself use more than one observer.
Private API approach
If you don't mind messing with private API, any object can provide you with an opaque pointed to its observers data with use of NSObject's observationInfo property. Under the hood the pointer holds an object which in turn holds an array of so-called observances - special data structure to describe a single subscription (every invocation of -[NSObject addObserver:forKeyPath:options:context:] creates a new instance in the array, even if all arguments are the same). An observance memory layout (at the time of writing this answer at least) looks something like this:
@interface NSKeyValueObservance: NSObject {
id _observer;
NSKeyValueProperty *_property;
void *_context;
id originalObservable;
}
The combination of these variables is the information you are looking for. Of course you cannot reliably extract this data, because it's private API, however the following contract keeps working since ages ago:
observationInfo should have an NSArray ivar which holds observances data;
_observer, _property and _context ivars names are as is;
_context and _observer can be compared with the desired data by pointer addresses;
_property ivar has _keyPath of type NSString to compare with the desired key-path;
With that in mind you can extend NSObject with a category and implement a convenient method which checks whether certain combination is present or not.
I don't mind sharing my own implementation but it's a little too much to fit in a single SO answer. You can check it out in my gists page.
Be advised, that this implementation performs ivars lookup by name and (sometimes) types. It is somewhat safer than just directly using ivar offsets, but still very fragile.
Here is a simple example of how to use it:
NSObject *observable = [NSObject new];
NSObject *observer = [NSObject new];
void *observerContext = &observerContext;
[observable addObserver:observer forKeyPath:@"observationInfo" options:NSKeyValueObservingOptionNew context:observerContext];
[observable addObserver:observer forKeyPath:@"observationInfo" options:NSKeyValueObservingOptionNew context:nil];
[observable addObserver:observer forKeyPath:@"observationInfo" options:NSKeyValueObservingOptionNew context:observerContext];
// Removes only 2 observances (subscriptions) where all parts matches (context, keyPath and observer instance)
while ([observable tdw_hasObserver:observer forKeyPath:@"observationInfo" context:observerContext error:nil]) {
[observable removeObserver:observer forKeyPath:@"observationInfo" context:observerContext];
}
Two-Sentence documentation
If context and/or keyPath arguments are nil, the implementation looks for subscriptions with any context and/or keyPath.
In case of any (expected) error, the method returns NO and writes error details to the error object, if corresponding pointer is provided. Expected errors include some minor changes in the private API (but far from any).