如何避免添加多个NSNotification观察者?

44

目前API似乎没有提供一种方法来检测是否已经为特定的NSNotification添加了观察者。除了在您的端上维护一个标志以进行跟踪之外,避免添加多个NSNotification观察者的最佳方法是什么?是否已经有人创建了一个类别来方便这个操作?


你能举个例子说明可能发生这种情况,或者为什么这会成为一个问题吗? - jscs
5个回答

73

防止重复添加观察者的一种方法是在再次添加之前明确调用目标/选择器的removeObserver方法。我想你可以将此作为类别方法添加:

@interface NSNotificationCenter (UniqueNotif)

- (void)addUniqueObserver:(id)observer selector:(SEL)selector name:(NSString *)name object:(id)object {

        [[NSNotificationCenter defaultCenter] removeObserver:observer name:name object:object];
        [[NSNotificationCenter defaultCenter] addObserver:observer selector:selector name:name object:object];

}

@end

这假设你只会为每个通知名称添加一个唯一的观察者,因为它会删除该通知名称下的任何现有观察者。


1
你在这里非常有趣、非标准地使用了“observer”和“target”。在addObserver...中,“observer”表示将在通知发布时接收消息的对象,而不是构成该消息的方法。通知中没有“target”的概念。你所称之为“target”的东西在文档中被称为“observer”。 - jscs
3
目标变量指示运行时应在哪里查找对象。我将参数名称“target”更改为“observer”,以消除任何潜在的混淆。 - futureelite7
1
虽然我的“你无法回答”的说法可能过于字面(一个对象无法检测到它的观察者),但这个答案最多只能算是不良实践。对于 OP 来说更好的建议是使用与发布和观察通知不同的实现方式来处理此用例。如果通知仅打算有单个观察者,则使用委托和协议不仅可以强制执行此操作,而且可以在对象和其委托之间的关系中清晰地表达出来。 - XJones
6
@XJones,这与通知发布给一个观察者无关。这与观察者不会观察到同一通知两次有关。通过这种方法,您仍然可以将多个观察者关联到一个通知上。 - SirRupertIII
2
是的,我误解了问题。这个答案确实解决了OP的问题,但感觉有点像绕过根本问题的方法 - 例如,为什么对象一开始就要观察同一个通知多次? - XJones

20

Swift 5:


Swift 5:
import Foundation

extension NotificationCenter {
  func setObserver(_ observer: Any, selector: Selector, name: Notification.Name, object: Any?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}

Swift 3-4:

import Foundation

extension NotificationCenter {
  func setObserver(_ observer: AnyObject, selector: Selector, name: NSNotification.Name, object: AnyObject?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}

Swift 2:

import Foundation

extension NSNotificationCenter {
  func setObserver(observer: AnyObject, selector: Selector, name: String?, object: AnyObject?) {
    removeObserver(observer, name: name, object: object)
    addObserver(observer, selector: selector, name: name, object: object)
  }
}

你能否给我一个在Swift 3.0中使用这个扩展添加观察者的例子? - Himanshu Moradiya
@Fattie removeObserver(self, forKeyPath: tag)?你在哪里找到的? - dimpiax

5

对我来说,带有extension NotificationCenter { ... }的被点赞的答案并没有起作用,因为每次发布通知时,我的应用程序都会创建一个新的viewController实例(其中包含一个Notification观察者),所以在新的viewController实例上删除观察者显然不起作用。之前具有Notification观察器的viewController实例将被调用。

以下内容适用于我,因为这是在视图消失时立即移除Notification Observer。

// Notification observer added 

override func viewWillAppear(_ animated: Bool) {

    NotificationCenter.default.addObserver(self, selector: #selector(self.someFunc(notification:)), name: Notification.Name("myNotification"), object: nil)


}


// Notification observer removed 

override func viewWillDisappear(_ animated: Bool) {

    NotificationCenter.default.removeObserver(self, name: Notification.Name("myNotification"), object: nil)


}

2
在编程中,建议在viewDidAppear中使用更好的addObserver而不是viewWillAppear - dimpiax
@dimpiax 为什么?谢谢。 - user5306470
因为这是出现视图控制器的阶段,所以如果您没有合理的情况将其放置在 viewWillAppear 中(可能只是为了动画),则必须将其放置在函数 viewDidAppear 中,当视图控制器出现并准备好与用户交互时。 - dimpiax

1

好吧,应该查看苹果文档。
https://developer.apple.com/documentation/foundation/notificationcenter/1411723-addobserver

let center = NSNotificationCenter.defaultCenter
var tokenOpt: NSObjectProtocol?
tokenOpt = center.addObserverForName("OneTimeNotification", object: nil, queue: nil) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}

为了确保通知如果已经存在就不再添加

if let token = tokenOpt{
  center.removeObserver(token)
}
tokenOpt = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
}

0

Swift 5:

根据@futureelite7和@dimpiax之前的答案,对Swift 5进行了一些微调。

extension NotificationCenter {
    func setObserver(_ observer: AnyObject, selector: Selector, name: NSNotification.Name, object: AnyObject?) {
        NotificationCenter.default.removeObserver(observer,
                                                  name: name,
                                                  object: object)
        NotificationCenter.default.addObserver(observer,
                                               selector: selector,
                                               name: name,
                                               object: object)
    }
}

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