Swift MacOX - Popover segue 创建多个视图控制器实例而不在它们被关闭时销毁它们

Swift MacOX - Popover segue creates multiple view controller instances without destroying them when they are dismissed

我正在像这样在情节提要中创建弹出式样式的视图控制器

然后我点击按钮,视图控制器显示,当我点击外面的任何地方时,视图控制器是 "dismissed"。

然而,当我再次单击该按钮时,视图控制器的一个新实例启动,而前一个实例仍然是 运行。我试过 deinit 但是当视图控制器是 "dismissed".

时它没有被调用

如何在点击外部时销毁视图控制器实例,或者 "show" 已经创建的实例?

我在视图控制器中的代码:

class FileTransViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
        timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
        //print(123)
        print("\(self)")
    }

    deinit {
        print("destroyed")
        if let timer = timer {
            timer.invalidate()
        }
    }

    @objc func updateProgress() {
        print("updating progress")
    }

}

问题与弹出窗口无关。你正在泄漏,因为你保留了计时器,而计时器保留了你——一个经典的保留循环。

要打破循环,您必须使计时器无效。您不能在 deinit 中执行此操作,因为根据定义,只有在您打破循环之后才能调用它。 NSPopover.willCloseNotification 可能是个好机会。

正如@matt 所说,您的保留周期有问题。您可以使用 Timer 和块来避免它,您可以在块中为 self

声明 weak 引用
timer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] timer in
    guard let self = self else {
        timer.invalidate()
        return
    }
    print("updating progress")
}

在这种情况下你也不需要 deinit 因为你在 guardelse 块中使计时器无效而且你也不需要变量 timer 如果你不想在其他地方手动使计时器无效,你可以只写 Timer

override func viewDidLoad() {
    super.viewDidLoad()
    Timer.scheduledTimer(...) { ... }
}