NSNotificationCenter : list of observers?

2019-01-18 18:05发布

问题:

Is it possible to get the list of observers (objects and selectors) for a given notification name? (NSNotificationCenter)

回答1:

I don't think there is an (official) way of retrieving the list of observers for a given notification name from NSNotificationCenter. However, you could create a subclass of NSNotificationCenter and then override the following methods:

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

In the overriding implementations of the instance methods, you would then keep track of the observers for a given notification name using a dictionary. In each overridden instance method you would finally call NSNotificationCenter's respective super method. Additionally, you would provide a method to retrieve your own list of observers for the given name, for example:

- (id)observerForNotificationName:(NSString *)name

However, there are two issues with this approach: first, NSMutableDictionary would retain all observers in a naive implementation, which is probably not the same behavior NSNotificationCenter implements. Second, you would have to change the code that gets the default notification center by [NSNotificationCenter defaultCenter] (or any other NSNotificationCenter instance) so as to use your custom subclass.

Note that the first issue is solvable using a CFDictionary with weak reference callbacks, a container class with a weak reference to the respective observer, or, if you are in a garbage collected environment on Mac OS X, an NSHashTable.



回答2:

(iOS 9, Swift 3) If you want to find out which observers are currently registered in NotificationCenter, break and print its debug description:

(lldb) e print(NotificationCenter.default.debugDescription)

Each line of the output will contain (Notification) Name, Object, Observer, Options. Multiple calls to NotificationCenter.default.addObserver with some NSNotification.Name will result in multiple entries in this list.


NB. while this might prove useful information when debugging, I would not advise to manage observers at runtime using this output.


(source: answer based on useyourloaf)



回答3:

There is no public API to query NSNotificationCenter about the list of current observers for any object or notification.

The previous answer outlines a solution and goes to some level of detail regarding the ownership of observers, in a subclass of NSNotificationCenter designed to collect and provide such information.

However, this solution can only be used with your own code, that will call the subclass of NSNotiicationCenter. What about other code, both in the system and external libraries who use the base NSNotificationCenter for registering/unregistering for notifications?

I suggest instead of subclassing NSNotificationCenter, using a bit of low-level ObjC to swizzle the method implementations of original NSNotifictionCenter, replacing them with our own implementations, that will work more-or-less as described in the previous answer, and will call the original implementations as their last act.

Here's how to do this: http://nshipster.com/method-swizzling/

Then, you can be sure you get ALL the observers of some notification, and that your code is portable and usable with 3rd party code that directly uses NSNotificationCenter.



回答4:

Instead of using NSNotificationCenter, you can try this ObserversCenter. And you can get the list of observers.

About ObserverCenter:

  1. it implements an multi-observer pattern as NSNotificationCenter;
  2. it decouple the observed and observers, so they don't know each other;
  3. you can subscribe on a specified key;
  4. you can call the real interface when making a notification.


回答5:

I created a category on NSNotificationCenter and swizzled the addObserver:::: method.

This is only for debugging and should never be in production code as it will lead to retain cycles

@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


回答6:

Have you tried the observationInfo property of NSObject?

observationInfo
Returns a pointer that identifies information about all of the observers that are registered with the receiver.