NSNotificationCenter:观察者列表?

37

有没有可能获取给定通知名称的观察者列表(对象和选择器)?(NSNotificationCenter)


3
请尝试由0xced(http://stackoverflow.com/users/21698/0xced)提供的解决方案 "http://stackoverflow.com/a/13274854/505735"。 - Muhammad Usman Aleem
这应该会对你有所帮助 - https://dev59.com/Lmkv5IYBdhLWcg3wxz2A#34692724 - Zayin Krige
7个回答

29

(iOS 9,Swift 3) 如果您想查找当前在NotificationCenter中注册的观察者,请打断并打印其调试描述:

(iOS 9,Swift 3) 如果您想查找当前在NotificationCenter中注册的观察者,请打断并打印其调试描述:

(lldb) e print(NotificationCenter.default.debugDescription)
每一行输出都将包含(通知)名称对象观察者选项。使用一些NSNotification.Name多次调用NotificationCenter.default.addObserver将在此列表中生成多个条目。
注:虽然这可能在调试时证明很有用,但我不建议使用此输出来在运行时管理观察者。
(来源:answer,基于useyourloaf

1
(lldb) e [NSNotificationCenter defaultCenter].debugDescription 对于 obj-c 可以工作。 - rstackhouse

16

我认为没有(官方的)方法可以从 NSNotificationCenter 中检索给定通知名称的观察者列表。但是,您可以创建 NSNotificationCenter 的子类,然后覆盖以下方法:

  • + defaultCenter
  • - addObserver:selector:name:object
  • - addObserverForName:object:queue:usingBlock:
  • - removeObserver:
  • - removeObserver:name:object

在这些实例方法的重写实现中,您可以使用字典跟踪给定通知名称的观察者。在每个重写的实例方法中,最后调用 NSNotificationCenter 相应的 super 方法。此外,您还将提供一个方法来检索给定名称的观察者列表,例如:

- (id)observerForNotificationName:(NSString *)name
然而,该方法存在两个问题:首先,在一个简单的实现中,NSMutableDictionary 会保留所有的观察者,这可能不是 NSNotificationCenter 实现的相同行为。其次,您需要更改获取默认通知中心的代码,即使用自定义子类替换 [NSNotificationCenter defaultCenter] (或任何其他 NSNotificationCenter 实例)。
请注意,第一个问题可以通过使用具有弱引用回调的 CFDictionary、带有对应观察者的弱引用的 容器类,或者在 Mac OS X 的垃圾回收环境中使用 NSHashTable 来解决。

1
NSHashTable 只有在使用垃圾回收(Garbage Collection)时才有用处,这非常不幸。 - Rob Keniger

5
没有公共API可以查询任何对象或通知的当前观察者列表与NSNotificationCenter。之前的答案概述了一个解决方案,并详细介绍了在 NSNotificationCenter 的子类中拥有观察者所有权的问题,该子类旨在收集和提供这些信息。然而,这种解决方案只能用于您自己的代码,即将调用 NSNotiicationCenter 的子类。那么其他代码呢?无论是系统中的还是使用基本 NSNotificationCenter 进行注册/注销通知的外部库?
我建议不要使用 NSNotificationCenter 的子类,而是使用一些低级别的ObjC来控制原始 NSNotifictionCenter 的方法实现,用我们自己的实现替换它们。这些实现将更多或少地按照先前答案中描述的方式工作,并将最后调用原始实现。
以下是如何完成此操作:http://nshipster.com/method-swizzling/ 然后,您可以确保获取某个通知的所有观察者,并且您的代码是可移植的,并可与直接使用 NSNotificationCenter 的第三方代码一起使用。

1

我在NSNotificationCenter上创建了一个类别,并交换了addObserver::::方法。

这只是用于调试,不应该出现在生产代码中,因为它会导致保留周期。

@interface NSNotificationCenter (Tracking)
@property (nonatomic) NSMutableArray <NSDictionary *> * observers;
@end


#import <JRSwizzle/JRSwizzle.h>
@implementation NSNotificationCenter (Tracking)

+ (void)initialize {
    [super initialize];
    [self jr_swizzleMethod:@selector(addObserver:selector:name:object:)
                withMethod:@selector(SNaddObserver:selector:name:object:)
                     error:nil];
}

- (void)SNaddObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject {
    NSDictionary *obs = @{@"observer"   :observer,
                          @"selector"   :NSStringFromSelector(aSelector),
                          @"name"       :aName
                          };
    DDLogDebug(@"observer added : %@", obs);
    [[self observers] addObject:obs];
    [self SNaddObserver:observer selector:aSelector name:aName object:anObject];
}

- (NSMutableArray <NSDictionary *> *) observers{
    static NSMutableArray <NSDictionary *> * _observers = nil;
    if (!_observers) {
        _observers = [NSMutableArray new];
    }
    return _observers;
}

@end

1
通过一些增强,这可能成为一个可行的解决方案。1)使用一些允许弱引用的CF集合,而不是您的NSDictionary-以便不会创建更多对观察对象的引用。2)使您的SNAddObserver线程安全(使用dispatch_once来创建您的集合)。3)定期清理,因为观察者被删除了。 - Motti Shneor
@MottiShneor 我一直在寻找类似于弱引用cf集合的东西。不过这真的可能吗?如果有任何资源,那就太好了。 - pnizzle
1
@pnizzle 试试 NSMapTable?https://developer.apple.com/documentation/foundation/nsmaptable - Andrew
是的,NSMapTable 是解决这个问题的更现代化的方案,可能更加适合,因为它不会保留观察者。 - Zayin Krige

1

不要使用NSNotificationCenter,你可以尝试使用ObserversCenter。你可以获得观察者列表。

关于ObserverCenter:

  1. 它实现了类似NSNotificationCenter的多观察者模式;
  2. 它解耦了被观察者和观察者,所以它们彼此不知道;
  3. 你可以订阅一个指定的键;
  4. 在进行通知时,你可以调用真正的接口。

1
NSNotificationCenter也是跨应用(进程)的,具有许多任何第三方机制都无法模仿的系统级行为。您不能使用第三方机制观察Cocoa和Cocoa-Touch标准通知(例如appWillbecomeForeground等)。因此,最好的方法是小心地扩展现有方法。 - Motti Shneor

1
与@PDK的答案类似,po NotificationCenter.default(或适当的实例)将提供调试信息:
<NSNotificationCenter:0x600000f84310>
Name, Object, Observer, Options
UIApplicationSimpleRemoteActionNotification, 0x7faac90069a0, 0x600000f84fc0, 1400
NSTextStorageDidProcessEditingNotification, 0x7faacd80d080, 0x600001808a00, 1400
com.apple.ManagedConfiguration.profileListChanged, 0x600000b0e280, 0x600000b0e280, 1400
UIApplicationDidBecomeActiveNotification, 0x7faac90069a0, 0x600000ff75d0, 1400
UIApplicationDidBecomeActiveNotification, 0x7faac90069a0, 0x600001880d00, 1400

这适用于Xcode 12,在旧版本中,可能需要使用debugDescription


-2
你尝试过使用NSObject的observationInfo属性吗?
observationInfo
Returns a pointer that identifies information about all of the observers that are registered with the receiver.

那是用于键值观察的,我认为不是NSNotificationCenter的观察者。 - nmr
你可能是对的。我还没有尝试过使用NSNotificationCenter。 - Erwan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接