首页 > 解决方案 > NotificationCenter.default.addObserver 不断被 Unwind Segue 调用多次

问题描述

我正在使用 show segue 和 unwind segue 在两个iOS视图控制器 VC1 和 VC2 之间导航。在viewDidLoad()VC2 中,我让 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()都会调用一次额外的时间,例如,在第四次返回时segueaddObserver 被调用 4 次;在第五次segue、五次等。即使应用程序被发送到后台并被调用,也会发生这种行为。它会记住在上一个会话中发生了多少次 segue,并从那里获取计数。

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

我试图在展开 segueing 后将 VC2 设置为 nil。

期待任何指导。

标签: iosswiftaddobserver

解决方案


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

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

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() { ... }
}

如果我然后进入和离开这个视图控制器 3 次,然后单击在此处输入图像描述“调试内存图”按钮,我将看到以下内容:

在此处输入图像描述

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

在这种情况下,因为我在“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()
}

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

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


推荐阅读