Swift中如何避免重复添加NotificationCenter观察者?
Swift 避免重复添加 NotificationCenter 观察者的解决方案
你遇到的问题是因为重复添加了完全相同的观察者(同一对象、同一selector、同一通知名、同一object),导致通知触发时回调函数被多次执行。以下是几种可靠的解决方法:
方法1:添加前先移除对应观察者
NotificationCenter 的 removeObserver 方法即使在没有对应观察者的情况下调用也不会崩溃,所以可以直接在添加前先移除,确保只会存在一个观察者实例:
let notificationName = NSNotification.Name(rawValue: "SameObserver") // 先移除已存在的对应观察者 NotificationCenter.default.removeObserver(self, name: notificationName, object: nil) // 再添加新的观察者 NotificationCenter.default.addObserver(self, selector: #selector(myFunction(notification:)), name: notificationName, object: nil)
注意:removeObserver 的参数要和 addObserver 完全匹配,这样才能精准移除目标观察者。
方法2:使用Block式观察者并保存引用
如果使用Block方式添加观察者,NotificationCenter 会返回一个 NSObjectProtocol 类型的观察者对象,我们可以将其保存在实例变量中,添加前检查是否已存在:
// 定义实例变量存储观察者引用 private var myNotificationObserver: NSObjectProtocol? func setupNotificationObserver() { // 如果观察者已存在,直接返回不重复添加 guard myNotificationObserver == nil else { return } let notificationName = NSNotification.Name(rawValue: "SameObserver") myNotificationObserver = NotificationCenter.default.addObserver( forName: notificationName, object: nil, queue: .main ) { [weak self] notification in self?.myFunction(notification: notification) } } // 记得在对象销毁时移除观察者 deinit { if let observer = myNotificationObserver { NotificationCenter.default.removeObserver(observer) } } @objc func myFunction(notification: Notification) { // 你的业务逻辑 }
这种方式需要注意用 [weak self] 避免循环引用,同时在 deinit 中手动移除观察者,防止内存泄漏。
方法3:通过Runtime检查观察者(不推荐生产环境)
NotificationCenter 没有公开API让我们直接查询已添加的观察者,不过可以通过Runtime访问其私有属性来实现检查。但这种方法依赖苹果的内部实现,可能在iOS版本更新后失效,仅作参考:
import ObjectiveC extension NotificationCenter { func hasExistingObserver(for target: Any, name: NSNotification.Name?, object: Any?, selector: Selector) -> Bool { guard let observers = self.value(forKey: "_observers") as? [AnyObject] else { return false } for observer in observers { let observerTarget = observer.value(forKey: "_observer") as? NSObject let observerSelector = observer.value(forKey: "_selector") as? Selector let observerName = observer.value(forKey: "_name") as? NSNotification.Name let observerObject = observer.value(forKey: "_object") if observerTarget === target as? NSObject, observerSelector == selector, observerName == name, observerObject === object { return true } } return false } }
使用示例:
let notificationName = NSNotification.Name(rawValue: "SameObserver") let targetSelector = #selector(myFunction(notification:)) if !NotificationCenter.default.hasExistingObserver(for: self, name: notificationName, object: nil, selector: targetSelector) { NotificationCenter.default.addObserver(self, selector: targetSelector, name: notificationName, object: nil) }
总结
优先推荐方法1和方法2,这两种方法简单可靠,没有私有API的风险,能有效避免重复添加观察者导致的多次回调问题。
内容的提问来源于stack exchange,提问作者Antonio Labra
相关产品推荐
相关产品推荐

