Fangming
Fangming

Reputation: 25280

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

I'm creating a popover style view controller in storyboard like this

enter image description here

Then I click the button, the view controller shows and when I click anywhere outside, the view controller is "dismissed".

However, when I click the button again, a new instance of the view controller is lunched and the previous one is still running. I have tried deinit but it's not getting called when the view controller is "dismissed".

How can I either destroy the view controller instance when clicking outside, or "show" the already created instance?

My code in the view controller:

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")
    }

}

Upvotes: 2

Views: 247

Answers (2)

Robert Dresler
Robert Dresler

Reputation: 11210

As @matt says, you have problem with retain cycle. You can avoid it using Timer with block where you can declare weak reference for self

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

You also don't need deinit in this case since you're invalidating timer inside guard's else block and you also don't need variable timer and you can just write Timer if you don't want to invalidate timer manually somewhere else

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

Upvotes: 1

matt
matt

Reputation: 536027

The problem has nothing to do with popovers. You are leaking because you retain the timer while the timer retains you — a classic retain cycle.

To break the cycle, you must invalidate the timer. You cannot do this in deinit, because by definition it cannot be called until after you break the cycle. NSPopover.willCloseNotification might be a good opportunity.

Upvotes: 3

Related Questions