## Long Story Short
Take `NSObject` category from [here][1] and use it like this:
```lang-objc
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:
```lang-objc
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`][2] 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:]`][3] 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:
```lang-objc
@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:
1. `observationInfo` should have an `NSArray` ivar which holds observances data;
2. `_observer`, `_property` and `_context` ivars names are as is;
3. `_context` and `_observer` can be compared with the desired data by pointer addresses;
4. `_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][1].
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:
```lang-objc
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).
[1]:
[2]:
[To see links please register here]
[3]:
[To see links please register here]