NotificationCenter.default.addObserver 不断被 Unwind Segue 调用多次

Posted

技术标签:

【中文标题】NotificationCenter.default.addObserver 不断被 Unwind Segue 调用多次【英文标题】:NotificationCenter.default.addObserver keep getting called multiple times with Unwind Segue 【发布时间】:2019-01-25 00:29:53 【问题描述】:

我正在使用 show segue 和 unwind segue 在两个 ios viewControllers,VC1 和 VC2 之间导航。在 VC2 的viewDidLoad() 中,我让 VC2 成为观察者。这是我的代码:

override func viewDidLoad() 
    super.viewDidLoad()

    NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "buzzer updated"), object: nil, queue: OperationQueue.main)  _ in
        print("set beeperViewImage")
    

每次我使用 unwind segue 从 VC2 回到 VC1 时,addObserver() 都会被调用一次,例如,在第四次返回时,segue addObserver 会被调用 4 次;在第五个segue,五次,等等。即使应用程序被发送到后台并被召回,这种行为也会发生。它会记住在上一个会话中发生了多少次 segue,并从那里获取计数。

我在VC1中多次调用没有问题,这是初始VC。

我尝试在 unwind segueing 后将 VC2 设置为 nil。

期待任何指导。

【问题讨论】:

在展开 VC2 时移除观察者。只需将卸妆液放入 deinit 中即可。 id 你的 vc2 deinit 没有被调用,在某个地方你保持你的 vc2 的强引用。所以观察者没有被删除。在第二个、第三个展开观察者不断增加。 @karthik - “在展开 VC2 时移除观察者。只需将移除器放入 deinit 中”......从 iOS 9 开始,我们不再需要显式移除 NotificationCenter 观察者。如果他确实有一个强引用循环,无论如何deinit 永远不会被调用。他需要弄清楚是什么建立了强引用循环并解决这个问题。然后,不需要移除观察者,因为它会自动发生跨度> 【参考方案1】:

这无疑是你的视图控制器没有被释放的情况。也许你有一个很强的参考周期。

例如,考虑这个无害的例子:

extension Notification.Name 
    static let buzzer = Notification.Name(rawValue: Bundle.main.bundleIdentifier! + ".buzzer")


class SecondViewController: UIViewController 

    override func viewDidLoad() 
        super.viewDidLoad()

        NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main)  _ in
            self.foo()
        
    

    func foo()  ... 

如果我然后进入和离开这个视图控制器三次,然后点击“调试内存图”按钮,我将看到以下内容:

我可以在左侧面板中看到我的第二个视图控制器的三个实例,如果它们被正确释放,它们就不会出现在那里。当我单击该面板中的任何一个时,我可以看到一个可视化图表,其中仍然对相关视图控制器有很强的引用。

在这种情况下,因为我在“Product”»“Scheme”»“Edit Scheme...”»“Run”»“Diagnostics”»“Logging”下打开了“Malloc Stack”功能,所以我可以看到最右边面板中的堆栈跟踪,甚至可以单击 箭头按钮并转到有问题的代码:

在这个特定示例中,问题在于我(出于说明目的故意)引入了一个持久的强引用,其中通知中心保持对self 的强引用,这是由观察者的关闭所施加的。通过在该闭包中使用[weak self] 模式,这很容易解决:

NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main)  [weak self] _ in
    self?.foo()

现在,我不知道这是否是您案例中强引用循环的来源,因为您的 sn-p 中的代码实际上并未引用 self。也许您在与我们共享代码时简化了您的代码 sn-p。也许你有一些完全不同的东西来保持对你的视图控制器的引用。

但是通过使用“Debug Memory Graph”按钮,您不仅可以 (a) 确认内存中确实存在多个相关视图控制器的实例;而且(b)确定是什么建立了这种强有力的参考。从那里,您可以诊断问题的根本原因。但是您问题中的代码不足以产生此问题,但我怀疑某处存在强引用循环。

【讨论】:

【参考方案2】:

感谢大家对我的问题的cmets。基于它们,我开始寻找可能保留在我的 VC2 上的东西。事实证明,在我的 VC2 viewWillAppear() 中读取蓝牙无线电链接的调用是罪魁祸首,但我不明白为什么:

self.radio?.peripheral?.readValue(for: (self.radio?.buzzerChar)!)

删除上述代码行后一切正常。再次感谢您指出搜索的方向。

【讨论】:

以上是关于NotificationCenter.default.addObserver 不断被 Unwind Segue 调用多次的主要内容,如果未能解决你的问题,请参考以下文章