if you tell an objective c object to removeObservers: for a key path and that key path has not been registered, it cracks the sads. like -
\'Cannot remove an observe
Put a try catch around your removeObserver call
@try{
[someObject removeObserver:someObserver forKeyPath:somePath];
}@catch(id anException){
//do nothing, obviously it wasn't attached because an exception was thrown
}
In my opinion - this works similar to retainCount mechanism. You can't be sure that at the current moment you have your observer. Even if you check: self.observationInfo - you can't know for sure that you will have/won't have observers in future.
Like retainCount. Maybe the observationInfo method is not exactly that kind of useless, but I only use it in debug purposes.
So as a result - you just have to do it like in memory management. If you added an observer - just remove it when you don't need it. Like using viewWillAppear/viewWillDisappear etc. methods. E.g:
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self addObserver:nil forKeyPath:@"" options:NSKeyValueObservingOptionNew context:nil];
}
-(void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self removeObserver:nil forKeyPath:@""];
}
And it you need some specific checks - implement your own class that handles an array of observers and use it for your checks.
When you add an observer to an object you could add it to a NSMutableArray
like this:
- (void)addObservedObject:(id)object {
if (![_observedObjects containsObject:object]) {
[_observedObjects addObject:object];
}
}
If you want to unobserve the objects you can do something like:
for (id object in _observedObjects) {
if ([object isKindOfClass:[MyClass class]]) {
MyClass *myObject = (MyClass *)object;
[self unobserveMethod:myObject];
}
}
[_observedObjects removeAllObjects];
Remember, if you unobserve a single object remove it from the _observedObjects
array:
- (void)removeObservedObject:(id)object {
if ([_observedObjects containsObject:object]) {
[_observedObjects removeObject:object];
}
}
In addition to Adam's answer I would like to suggest to use macro like this
#define SafeRemoveObserver(sender, observer, keyPath) \
@try{\
[sender removeObserver:observer forKeyPath:keyPath];\
}@catch(id anException){\
}
example of usage
- (void)dealloc {
SafeRemoveObserver(someObject, self, somePath);
}
FWIW, [someObject observationInfo] seems to be nil
if someObject
doesn't have any observers. I wouldn't trust this behavior, however, as I haven't seen it documented. Also, I don't know how to read observationInfo
to get specific observers.
I am not a fan of that try catch solution so what i do most of the time is that i create a subscribe and unsubscribe method for a specific notification inside that class. For example these two methods subcribe or unsubscribe the object to the global keyboard notification:
@interface ObjectA : NSObject
-(void)subscribeToKeyboardNotifications;
-(void)unsubscribeToKeyboardNotifications;
@end
Inside those methods i use a private property which is set to true or false depending on the subscription state like so:
@interface ObjectA()
@property (nonatomic,assign) BOOL subscribedToKeyboardNotification
@end
@implementation
-(void)subscribeToKeyboardNotifications {
if (!self.subscribedToKeyboardNotification) {
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardHide:) name:UIKeyboardWillHideNotification object:nil];
self.subscribedToKeyboardNotification = YES;
}
}
-(void)unsubscribeToKeyboardNotifications {
if (self.subscribedToKeyboardNotification) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
self.subscribedToKeyboardNotification = NO;
}
}
@end