我怎样才能知道一个对象是否有一个关键值观察者附加

如果您告诉一个目标 c 对象删除观察者: 对于一个键路径并且该键路径没有被注册,那么它将破解这个变量。就像..

无法删除键路径“ theKeyPath”的观察者,因为它没有注册为观察者

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

if (object has observer){
remove observer
}
else{
go on my merry way
}
59523 次浏览

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

如果您是在被观察对象的类中执行此操作,请停止。不管是什么在观察它,它都会继续观察下去。如果在观察者不知情的情况下切断了他的通知,那么可以预料事情会中断; 更具体地说,可以预料观察者的状态会失效,因为它没有从以前观察到的对象接收到更新。

如果你在观察对象的类中做这件事,只要记住你在观察哪个对象(或者,如果你只观察一个对象,无论你是否在观察它)。这是假设观察是动态的,在两个不相关的对象之间; 如果观察者拥有被观察者,只需在创建或保留被观察者之后添加观察者,并在释放被观察者之前删除观察者。

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

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

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

为什么?

你现在的问题是,你假设自己被观察了,而实际上并没有。这个物体没有开始观察。如果您希望您的类能够控制这个过程,那么您应该考虑使用通知中心。这样,您的类就可以完全控制何时可以观察数据。因此,它不在乎谁在看。

在 RemoveObserver 调用周围放置一个 try catch

@try{
[someObject removeObserver:someObserver forKeyPath:somePath];
}@catch(id anException){
//do nothing, obviously it wasn't attached because an exception was thrown
}

如果 someObject没有任何观察者,那么 [someObject observationInfo]似乎是 nil。然而,我不相信这种行为,因为我没有看到它的记录。而且,我不知道如何读取 observationInfo来获得特定的观察者。

当你将一个观察者添加到一个对象时,你可以像这样将它添加到一个 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];
}
}

在我看来,这种工作方式类似于 retainCount 机制。你不能确定在当前时刻你有你的观察者。 即使你检查: 自我观察信息-你不能确定你将来会有/不会有观察员。

就像 保留计数。也许 观测信息方法并不完全是那种毫无用处的方法,但是我只在调试目的中使用它。

因此,您只需要像在内存管理中那样做就可以了。如果你添加了一个观察者-当你不需要它的时候就删除它。比如使用 viewWillAppear/viewWillDisaper 等方法。例如:

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

而且你需要一些特定的检查——实现你自己的类来处理一个观察者数组并用它来进行检查。

我不是一个球迷,尝试赶上解决方案,所以 大多数时候,我所做的是为该类中的特定通知创建一个订阅和取消订阅方法。例如,这两种方法将对象订阅或取消订阅到全局键盘通知:

@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

如果没有观察者,返回 nil

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


}

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

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

用法范例

- (void)dealloc {
SafeRemoveObserver(someObject, self, somePath);
}

长话短说

给你中选取 NSObject类别,并像下面这样使用它:

if ([observable tdw_hasObserver:observer forKeyPath:@"key.path" context:nil error:nil]) {
[observable removeObserver:observer forKeyPath:@"key.path"];
} else {
// go on your merry way
}

公共空气污染指数方法

对于许多 基金会类,除了您的观察者之外没有其他任何观察者,您可以只检查 observationInfo属性值。当对象没有任何观察者或者不透明的 void *指针时,该属性返回空指针:

if (observable.observationInfo) {
[observable removeObserver:observer forKeyPath:@"key.path"];
} else {
// go on your merry way
}

然而,对于大多数 UIKit类和场景(您自己使用多个观察者) ,这种方法不起作用。


私人空气污染指数方法

如果您不介意使用私有 API,任何对象都可以使用 ABC0是 observationInfo属性为您提供指向其观察者数据的不透明对象。在引擎盖下面,指针持有一个对象,该对象又持有一个所谓的 仪式特殊数据结构的数组来描述一个 订阅(每次对 -[NSObject addObserver:forKeyPath:options:context:]的调用都会在数组中创建一个新的实例,即使 所有参数是相同的)。观察记忆布局(至少在写这个答案的时候)看起来是这样的:

@interface NSKeyValueObservance: NSObject {
id _observer;
NSKeyValueProperty *_property;
void *_context;
id originalObservable;
}

这些变量的组合就是您要寻找的信息。当然,你不能可靠地提取这些数据,因为它是私有 API,但是由于一些幼稚的假设,这个合同从很久以前就开始有效了:

  1. observationInfo应该有一个保存观测数据的 NSArray变量;
  2. _observer_property_context变种名称保持不变;
  3. _context_observer可以通过指针地址与所需数据进行比较;
  4. _property变体具有 NSString类型的 _keyPath,以便与所需的键路径进行比较;

考虑到这一点,您可以使用一个类别扩展 NSObject,并实现一个方便的方法来检查是否存在某种组合。

我不介意分享我自己的实现,但是用一个 SO 来回答有点太多了。你可以在 我的主旨页面中查看。 请注意,此实现根据名称和(有时)类型执行 ivars 查找。它比直接使用 ivar 偏移量要安全一些,但仍然非常脆弱。如果 API 的私有部分发生了任何变化,我不会相信实现不会崩溃。

下面是如何使用它的一个简单例子:

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];
}

两句话的文档

如果 context和/或 keyPath参数是 nil,则实现假定相应观测变量的 任何值满足搜索条件。

在出现任何(预期的)错误的情况下,如果提供了相应的指针,该方法将返回 NO并在 error对象中写入错误详细信息。预期的错误包括私有 API 中的一些微小更改(但远远没有任何更改)。

我还鼓励您在生产应用程序中使用 没有代码,而对于调试来说,这通常已经足够了。