Swift中从另一个ViewController移除通知观察者的问题
解决VCA通知观察者重复注册及跨VC移除的问题
首先得指出你之前尝试的问题所在:在VCB里调用instantiateViewController(withIdentifier:)得到的是全新的VCA实例,根本不是当前后台运行的那个VCA对象,所以移除操作完全没作用在正确的目标上,这就是为什么你的代码无效。
下面给你几个可行的解决方案,按推荐程度排序:
方案一:用闭包回调替代通知(最推荐)
这种一对一的VC间通信,闭包比通知更简洁,还能彻底避免观察者管理的麻烦:
- 在VCB里定义一个闭包属性:
class VCB: UIViewController { // 定义回调闭包,点击按钮时触发 var onTriggerAction: (() -> Void)? @IBAction func buttonPressed(_ sender: Any) { // 直接调用闭包触发VCA的方法 onTriggerAction?() } @IBAction func backButtonPressed(_ sender: Any) { dismiss(animated: true, completion: nil) } }
- 在VCA跳转VCB时,绑定闭包逻辑:
// VCA中present VCB的代码示例 let vcb = storyboard?.instantiateViewController(withIdentifier: "VCB") as! VCB // 用weak self避免循环引用 vcb.onTriggerAction = { [weak self] in self?.someFunction() } present(vcb, animated: true)
这样完全不需要处理通知观察者,逻辑清晰还不容易出错。
方案二:修正通知观察者的移除逻辑(如果坚持用通知)
第一步:获取正确的VCA实例
因为VCB是被VCA present出来的,所以VCB的presentingViewController就是你要操作的那个VCA实例,直接强转后操作即可:
@IBAction func backButtonPressed(_ sender: Any) { // 拿到当前后台的VCA实例 if let targetVCA = self.presentingViewController as? VCA { NotificationCenter.default.removeObserver(targetVCA, name: Notification.Name("name"), object: nil) } dismiss(animated: true, completion: nil) }
第二步:避免重复注册观察者
即使解决了移除问题,每次viewDidAppear都添加观察者还是会导致重复注册,建议在添加前先移除一次:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 先移除再添加,确保同一时刻只有一个观察者 NotificationCenter.default.removeObserver(self, name: Notification.Name("name"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(someFunction), name: Notification.Name("name"), object: nil) }
进阶优化:用Block式观察者(iOS 9+)
Block式观察者会返回一个token,能更精准地管理,还能避免Selector的写法:
class VCA: UIViewController { // 保存观察者token private var notificationToken: NSObjectProtocol? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 先移除旧的观察者 if let token = notificationToken { NotificationCenter.default.removeObserver(token) } // 添加Block式观察者 notificationToken = NotificationCenter.default.addObserver(forName: Notification.Name("name"), object: nil, queue: .main) { [weak self] _ in self?.someFunction() } } // 提供外部调用的移除方法 func cleanUpNotificationObserver() { if let token = notificationToken { NotificationCenter.default.removeObserver(token) notificationToken = nil } } }
然后在VCB的返回按钮方法里调用这个方法:
@IBAction func backButtonPressed(_ sender: Any) { if let targetVCA = self.presentingViewController as? VCA { targetVCA.cleanUpNotificationObserver() } dismiss(animated: true, completion: nil) }
内容的提问来源于stack exchange,提问作者kasperonline
相关产品推荐
相关产品推荐

