【问题标题】:When to use NSNotificationCenter何时使用 NSNotificationCenter
【发布时间】:2012-06-05 21:06:09
【问题描述】:

我希望对单个对象的多个事件有多个观察者(一对 N 关系)。

NSNotificationCenter 提供了实现此任务的机制。当用于我的问题时,该机制看起来有点矫枉过正。

如何在不使用NSNotificationCenter 的情况下手动操作:

- (void)addDelegate:(id<DelegateProtocol>)delegate;
- (void)removeDelegate:(id<DelegateProtocol>)delegate;

从我的对象中添加和删除观察者。

- (void)someEventFired:(NSObject<NSCopying> *)eventData
{
    for (id delegate in delegates) {
        NSObject *data = [eventData copy];
        [delegate someEventFired:data];
    }
}

这种机制简单易行,无需对象共享额外的字符串。

  • 除了NSNotificationCenter之外,iOS 框架中是否还有官方的 1 对 N 委托(如 C# 事件)模式?
  • 什么时候应该使用NSNotificationCenter,什么时候不应该使用?
  • 什么时候应该使用我在这里建议的实现,什么时候不应该使用?

【问题讨论】:

  • 我很少或曾经使用过 NSNotificationCenter ,但使用了与您描述的相同的方法。多年来,我已经在许多 iOS 应用程序(我会说超过 50 个应用程序)中使用它,到目前为止我还没有看到任何问题。一个可能的问题是您有时需要确保删除观察者,或者它们可能不会在预期的时候被释放,因为它们被代表的保管者保留。
  • 我认为即使 NSNotificationCenter 不是我们从 C# 中知道的方法,我们仍然应该专注于为我们正在工作的特定平台使用设计模式。也就是说,我现在在几个应用程序中使用了 NSNotificationCenter(虽然没有你那么多),而且我不认为代码通过它变得更糟。

标签: objective-c ios architecture observer-pattern nsnotificationcenter


【解决方案1】:

按照惯例,委托可能应该只用于 1:1 的关系。如果您确实需要此类功能的 1:N 关系,您有两种选择:

  1. 正如你所说,NSNotificationCenter。
  2. Key-Value Observing(也称为 KVO)。

如果您只关心对象的特定属性何时更改,则 KVO 是合适的。否则,你真的应该考虑使用NSNotificationCenter。您甚至可以通过将该对象传递给addObserver:selector:name:object: 方法来仅在特定对象发布该通知时收到通知。

Apple 在类似的场景中使用NSNotification(例如为UITextField 定义的通知,包括UITextFieldTextDidBeginEditingNotification、UITextFieldTextDidChangeNotification 和UITextFieldTextDidEndEditingNotification)。

【讨论】:

【解决方案2】:

使用通知就是广播:1 个发送者只是发送一个信息,而谁曾经收听过,就会收到它。很像广播电台,没有频道(让我们暂时忘记电话)

代表团是不同的。要求委托人做某事的对象通常需要该请求的结果,因此委托是一对一的通信,始终由对象而不是委托发起(而对象可以具有可以调用通知对象发起通信,即[tableView reloadData])。

所以如果发送者需要取回数据,那就是委托。如果发送者在广播后不关心任何事情,请使用通知。

如果您遇到这种情况,您需要委托,但有几个对象应该实现该协议。你应该有 1 个委托,它持有对其他对象的引用并代表发送者调用方法——或者你可以使用块。

【讨论】:

  • 今天我只有一个对象,所有 VC 共享并通知他们。
【解决方案3】:

NSNotificationCenter 对于您的建议并不过分,它正是正确的解决方案。它可以防止被观察对象必须知道或关心它的观察者,从而使您的代码更加松散耦合和清洁。

通知名称的共享字符串是微不足道的,如果您的观察者需要导入此标头来完成他们的工作,它们可以在共享常量文件或被观察对象的标头中定义。

【讨论】:

    【解决方案4】:

    您提出的解决方案既不比使用 NSNotificationCenter 简单,也不是线程安全的。

    为了使您的解决方案线程安全,您需要提供一种机制来防止在事件调度 for 循环运行时委托数组发生变化。

    您的解决方案还要求您在班级中维护代表数组。使用 NotificationCenter,您可以简单地使用默认中心,而无需在类中实现添加/删除方法。相反,实例可以注册自己以接收它们认为最合适的通知(选择器/块、队列、源)。您的源类不必担心这些细节。它只需要将自己注册为指定类型的通知源。使用块来处理通知真的很方便。

    通知中心的替代方法是使用 Key-Value-Observing,如果它满足您的用例需求。

    最终,您决定使用的机制取决于它如何最好地适用于您的特定用例。

    【讨论】:

      【解决方案5】:

      一对 N 的委托关系没有意义。来看看

      - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
      

      例如。如果这个对象真的有 n 个委托怎么办?它应该如何决定应该使用从所有代表那里获得的 n 个视图中的哪一个?代表正是这种一对一的原则。

      NSNotificationCenter 是正确的方法。只需使用

      addObserver:selector:name:object:
      

      分别

      postNotification:
      

      这绝对不是太多的代码。由于中心处理所有呼叫,因此对您来说非常容易。

      【讨论】:

        【解决方案6】:

        您不想将 NSNotificationCenter 用于系统范围事件以外的任何事情(例如键盘的出现或一些类似事件)。原因是它完全不是类型安全的,可以使所有内容都依赖于所有内容,并且您不再获得编译时检查或使用搜索结果。

        在我看来,KVO 不应该用于观察您正在收听的对象之外的变化,因为它有类似的缺点(没有编译时检查,如果您没有正确删除侦听器或注册两次,则会崩溃)。

        在我看来,您提出的 addDelegate/removeDelegate 模式完全是正确的路径,因为它具有维护类型安全和编译器检查并明确依赖关系的优点。唯一的问题是 Apple 没有为这种模式提供开箱即用的解决方案,因为您需要一个弱保留其元素的集合类型以避免保留循环。

        但是,请参阅我的 BMCommons framework 中的代码,它使用 BMNullableArray 和宏巧妙地解决了这个问题。有关这些宏的定义,请参见 BMCore.h 标头:

        BM_LISTENER_METHOD_DECLARATION(protocol)
        BM_LISTENER_METHOD_IMPLEMENTATION(protocol)
        

        该实现确保同一个监听器永远不会被添加两次,并且监听器被弱保留,即使他们忘记在释放时注销自己也不会导致任何崩溃(尽管我更喜欢用断言来捕捉这个条件,因为它是编程错误)。

        【讨论】:

          【解决方案7】:

          我说 NSNotificationCenter 应该始终在委托模型上使用,除非在您查询委托信息的情况下(例如-webView:shouldLoadRequest:)。与尝试使用委托相比,它更稳定,更易于实现,并且代码更简洁。另一种选择是块,这可能很好,但在内存管理方面可能会很痛苦。

          最后,这取决于你,但我认为 NSNotificationCenter 是几乎在任何情况下的最佳选择,即使只是用于多观察者功能。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-15
            • 1970-01-01
            • 1970-01-01
            • 2023-03-17
            • 1970-01-01
            相关资源
            最近更新 更多