NotificationCenter.default.addObserver通过Unwind Segue不断被调用

时间:2019-01-25 00:29:53

标签: ios swift addobserver

我正在使用show segue和unwind segue在两个iOS viewController 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")
    }
}

每次我使用unsegue segue从VC2返回VC1时,addObserver()被调用一个额外的时间,例如,在第四次返回segue时addObserver被调用4次;在第五个segue上进行五次,等等。即使将应用程序发送到后台并重新调用,也会发生这种情况。它会记住上一次会话发生了多少次segue,并从那里获取计数。

在最初的VC1中,我对多个调用没有问题。

我试图在取消锁定后将VC2设置为nil。

期待任何指导。

2 个答案:

答案 0 :(得分:0)

这无疑是没有释放视图控制器的情况。也许您的参考周期很长。

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

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次,然后单击enter image description here“调试内存图”按钮,则会看到以下内容:

enter image description here

我可以在左侧面板中看到我的第二个视图控制器的三个实例,如果它们被正确地重新分配,它们将不会显示在那里。当我在该面板中单击任意一个时,我可以看到一个可视化图形,该图形仍然对所讨论的视图控制器具有很强的参考价值。

在这种情况下,由于我打开了“产品”»“方案”»“编辑方案...”»“运行”»“诊断”»“日志记录”下的“ Malloc堆栈”功能,因此可以看到堆栈跟踪显示在最右边的面板中,甚至可以单击enter image description here箭头按钮,然后转到有问题的代码:

code

在此特定示例中,问题是我(故意地出于说明目的)引入了持久的强引用,其中通知中心保持了对self的强引用,这是由观察者的关闭造成的。通过在该闭包中使用[weak self]模式,可以轻松解决此问题:

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

现在,由于您的代码段中的代码实际上并未引用self,因此我不知道这是否是强引用周期的源头。与我们共享代码段时,也许您简化了代码段。也许您还有其他东西完全在引用您的视图控制器。

但是通过使用“调试内存图”按钮,您不仅可以(a)确认内存中确实存在相关视图控制器的多个实例;而且(b)找出建立该强引用的依据。从那里,您可以诊断出问题的根本原因是什么。但是您问题中的代码不足以产生此问题,但是我怀疑某个地方有很强的参考周期。

答案 1 :(得分:0)

谢谢大家对我的问题的评论。基于这些内容,我开始搜索VC2可能保留的内容。原来,在我的VC2 viewWillAppear()中读取蓝牙无线链接的调用是罪魁祸首,但我不明白为什么:

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

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