3

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
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • FWIW `nil`ing the timer would just remove the reference to it and still allow it trigger. Copied from [here](https://stackoverflow.com/questions/27416896/what-is-diffrence-between-self-timer-nil-vs-self-timer-invalidate-in-ios#comment43294396_27416962): *Memory management and ARC have nothing to do with why you should set it to `nil`. After invalidating the timer, `self.timer` is now referencing a useless timer. No further attempts should be made to use that value. Setting it to nil ensures that any further attempts to access `self.timer` will result in `nil`* – mfaani Aug 10 '17 at 19:05

2 Answers2

3

The scheduledTimer(withTimeInterval:repeats:block:) method:

After interval seconds have elapsed, the timer fires, executing block.

The invalidate() method:

Stops the timer from ever firing again

You are correct in your discovery that invalidating a timer will not interrupt a currently executing block, but will only prevent future executions of that block.

Adrian Bobrowski
  • 2,681
  • 1
  • 16
  • 26
Joshua Breeden
  • 1,844
  • 1
  • 16
  • 29
0

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.

mfaani
  • 33,269
  • 19
  • 164
  • 293