【问题标题】:NSNotificationCenter : list of observers?NSNotificationCenter:观察者名单?
【发布时间】:2012-04-27 07:42:11
【问题描述】:

是否可以获得给定通知名称的观察者列表(对象和选择器)? (NSNotificationCenter)

【问题讨论】:

标签: objective-c cocoa nsnotificationcenter


【解决方案1】:

(iOS 9, Swift 3) 如果你想知道当前在NotificationCenter 注册了哪些观察者,打破并打印它的调试描述:

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

输出的每一行将包含(通知)名称对象观察者选项。多次调用NotificationCenter.default.addObserver 和一些NSNotification.Name 将导致此列表中有多个条目。


注意。虽然这可能在调试时证明是有用的信息,但我不建议在运行时使用此输出来管理观察者。


(来源:answer 基于useyourloaf

【讨论】:

  • (lldb) e [NSNotificationCenter defaultCenter].debugDescription 适用于 obj-c。
【解决方案2】:

我认为没有(官方)方法可以从 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 实例)更改获取默认通知中心的代码,以便使用您的自定义子类。

请注意,第一个问题可以使用带有弱引用回调的CFDictionarycontainer class with a weak reference 来解决相应的观察者,或者,如果您在 Mac OS X 上的垃圾收集环境中,则可以使用 NSHashTable

【讨论】:

  • NSHashTable(非常不幸)仅在您使用垃圾收集时才有用。
【解决方案3】:

没有公共 API 可以查询 NSNotificationCenter 任何对象或通知的当前观察者列表。

上一个答案概述了一个解决方案,并在NSNotificationCenter 的子类中详细介绍了观察者的所有权,该子类旨在收集和提供此类信息。

但是,此解决方案只能与您自己的代码一起使用,该代码将调用NSNotiicationCenter 的子类。系统和外部库中使用基本NSNotificationCenter 注册/注销通知的其他代码呢?

我建议不要继承 NSNotificationCenter,而是使用一些低级 ObjC 来调整原始 NSNotifictionCenter 的方法实现,用我们自己的实现替换它们,这将或多或少地工作,如以前的答案,并将原始实现称为他们的最后一幕。

操作方法如下:http://nshipster.com/method-swizzling/

然后,您可以确保您获得了某个通知的所有观察者,并且您的代码是可移植的,并且可以与直接使用 NSNotificationCenter 的第三方代码一起使用。

【讨论】:

    【解决方案4】:

    你可以试试这个ObserversCenter,而不是使用 NSNotificationCenter。你可以得到观察者的名单。

    关于观察中心:

    1. 它实现了一个多观察者模式作为 NSNotificationCenter;
    2. 它将被观察者和观察者解耦,因此它们彼此不认识;
    3. 您可以订阅指定的密钥;
    4. 您可以在发出通知时调用真实界面。

    【讨论】:

    • NSNotificationCenter 也是跨应用程序(进程)的,并且具有许多任何 3rd 方机制都无法模仿的系统范围的行为。您无法使用 3rd 方机制观察 Cocoa 和 Cocoa-Touch 标准通知(例如 appWillbecomeForeground 等)。所以 - 最好的方法是小心地扩展现有的。
    【解决方案5】:

    我在 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)使用一些允许弱引用而不是您的 NSDictionary 的 CF 集合 - 这样就不会创建对观察对象的更多引用。 2) 使您的 SNAddObserver 线程安全(使用 dispatch_once 创建您的集合) 3) 随着观察者的移除,不时进行清理。
    • @MottiShneor 我一直在寻找类似于弱参考 cf 集合的东西。这真的可能吗?任何资源都会很棒
    • 是的,NSMapTable 是解决这个问题的更现代的解决方案,可能更适合,因为它不会保留观察者
    【解决方案6】:

    类似于@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 可能是必需的。

    【讨论】:

      【解决方案7】:

      你试过NSObject的observationInfo属性吗?

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

      【讨论】:

      • 我认为这是用于键/值观察,而不是 NSNotificationCenter 观察者。
      • 你可能是对的。我没有用 NSNotificationCenter 尝试过
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 2014-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多