Why does timer continue to execute after invalidation?

Viewed 429

If you run the code below, even after I invalidate the timer, the remaining code of the timer executes without any disruption. Why?

Is it because the closure has a strong reference to itself and is retained until it completely finishes itself? Or something else?

Does this mean invalidating a timer during its moment of execution does nothing?

class ViewController: UIViewController {

    var timer : Timer?
    let serialQueue = DispatchQueue(label: "com.createTimer.serial")

    override func viewDidLoad() {
        super.viewDidLoad()

        serialQueue.sync { [weak self] in
            self?.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { [weak self] _ in
                self?.someDummyFunc()
                print("yyy")
                print("\(self?.timer?.isValid)")
            })
        }
    }

    func someDummyFunc(){
        print("\(timer?.isValid)")
        print("xxx")
        timer?.invalidate()
    }
}

The prints that I get from running this code is:

Optional(true)
xxx
yyy
Optional(false) // timer.isValid is false !!!

Yet what I initially thought I would get is:

Optional(true)
xxx
2 Answers

Timer or not, any enqueued block will have to finish. Once it's enqueued there's no stopping it.

Suppose you had the following code inside a viewController's:

override func viewDidLoad() {
    super.viewDidLoad()

    let x = 10
    DispatchQueue.main.async { [weak self] in
        print(x)
        self?.someTaskWhichTakes3seconds()
        print(self?.view.backgroundColor)
        print(x + 2)
    }
}

and after viewDidLoad was called you immediately popped the viewController off the navigation stack (or had it deallocated somehow), in that case, still print(x+2) would happen. Why? Because the block is enqueued and it has to finish.

Related