Swapped [weak self] for [unowned self] in a repeating Timer block to get rid of the optional, then closed the screen. The next tick crashed.
Nothing mysterious. The run loop keeps a scheduled timer alive, and the timer keeps its block. So the closure outlives the controller unless someone calls invalidate(). unowned tells Swift the controller will outlive the closure, which is backwards here, and the book is plain that touching an unowned reference after deallocation is a runtime error.
Strong capture doesn't crash. It's worse in a quieter way. The controller can't go away while the timer holds the block, so the invalidate() I'd put in deinit never runs, and the timer ticks forever.
Weak is the one that works, and it still leaves a decision. Should this callback keep its owner alive? No. Then what does a tick with no owner mean? Here, nothing, and the timer should stop, so deinit calls invalidate() and the guard let self else { return } stays as a cheap check.
