142

如果你告诉一个客观的 c 对象 removeObservers:对于一个关键路径并且该关键路径尚未注册,它会破解悲伤。像 -

'不能从中删除关键路径“theKeyPath”的观察者,因为它没有注册为观察者。

有没有办法确定一个对象是否有注册的观察者,所以我可以这样做

if (object has observer){
  remove observer
}
else{
  go on my merry way
}
4

10 回答 10

316

在您的 removeObserver 调用周围放置一个 try catch

@try{
   [someObject removeObserver:someObserver forKeyPath:somePath];
}@catch(id anException){
   //do nothing, obviously it wasn't attached because an exception was thrown
}
于 2011-07-16T00:57:38.127 回答
37

真正的问题是为什么你不知道你是否在观察它。

如果您在被观察对象的类中执行此操作,请停止。无论在观察什么,它都希望继续观察它。如果您在观察者不知情的情况下切断了观察者的通知,预计事情会中断;更具体地说,期望观察者的状态过时,因为它没有从以前观察到的对象接收更新。

如果您在观察对象的类中执行此操作,只需记住您正在观察哪些对象(或者,如果您只观察一个对象,无论您是否正在观察它)。这是假设观察是动态的并且在两个原本不相关的对象之间;如果观察者拥有被观察者,只需在创建或保留观察者后添加观察者,并在释放观察者之前删除观察者。

作为观察者添加和删除对象通常应该发生在观察者的类中,而不是在被观察对象的类中。

于 2009-10-17T16:28:54.930 回答
25

FWIW,[someObject observationInfo]似乎nil没有someObject任何观察者。但是,我不相信这种行为,因为我没有看到它记录在案。另外,我不知道如何阅读observationInfo以获得特定的观察者。

于 2012-02-17T03:08:27.633 回答
4

唯一的方法是在添加观察者时设置一个标志。

于 2009-10-17T17:12:58.290 回答
4

当您将观察者添加到对象时,您可以将其添加到NSMutableArray如下所示:

- (void)addObservedObject:(id)object {
    if (![_observedObjects containsObject:object]) {
        [_observedObjects addObject:object];
    }
}

如果您想不观察对象,您可以执行以下操作:

for (id object in _observedObjects) {
    if ([object isKindOfClass:[MyClass class]]) {
        MyClass *myObject = (MyClass *)object;
        [self unobserveMethod:myObject];
    }
}
[_observedObjects removeAllObjects];

请记住,如果您未观察到单个对象,请将其从_observedObjects数组中删除:

- (void)removeObservedObject:(id)object {
    if ([_observedObjects containsObject:object]) {
        [_observedObjects removeObject:object];
    }
}
于 2014-06-12T12:34:45.227 回答
3

在我看来 - 这类似于 retainCount 机制。你不能确定在当前时刻你有你的观察者。即使您检查:self.observationInfo - 您也无法确定您将来会有/不会有观察者。

retainCount一样。也许observationInfo方法并不是那种无用的,但我只在调试目的时使用它。

所以结果 - 你只需要像在内存管理中那样做。如果您添加了观察者 - 只需在不需要时将其删除。就像使用 viewWillAppear/viewWillDisappear 等方法一样。例如:

-(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:@""];
}

而且您需要一些特定的检查 - 实现您自己的类来处理一组观察者并将其用于您的检查。

于 2015-07-03T11:34:13.743 回答
3

[someObject observationInfo]nil如果没有观察者则返回。

if ([tableMessage observationInfo] == nil)
{
   NSLog(@"add your observer");
}
else
{
  NSLog(@"remove your observer");

}
于 2015-11-26T11:44:42.897 回答
2

观察者模式的全部意义在于允许一个被观察的类被“密封”——不知道也不关心它是否被观察。您明确地试图打破这种模式。

为什么?

您遇到的问题是,您假设自己没有被观察。这个物体没有开始观察。如果你想让你的班级控制这个过程,那么你应该考虑使用通知中心。这样,您的班级就可以完全控制何时可以观察到数据。因此,它不在乎谁在看。

于 2009-10-18T16:51:30.713 回答
1

我不是那种 try catch 解决方案的粉丝,所以我大部分时间都在为该类中的特定通知创建订阅和取消订阅方法。例如,这两个方法订阅或取消订阅对象到全局键盘通知:

@interface ObjectA : NSObject
-(void)subscribeToKeyboardNotifications;
-(void)unsubscribeToKeyboardNotifications;
@end

在这些方法中,我使用了一个私有属性,该属性根据订阅状态设置为 true 或 false,如下所示:

@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
于 2015-09-22T07:11:17.243 回答
0

除了亚当的回答,我想建议使用这样的宏

#define SafeRemoveObserver(sender, observer, keyPath) \
@try{\
   [sender removeObserver:observer forKeyPath:keyPath];\
}@catch(id anException){\
}

使用示例

- (void)dealloc {
    SafeRemoveObserver(someObject, self, somePath);
}
于 2017-02-24T02:07:36.770 回答