openlang

unowned self in a repeating Timer just delays the crash

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.

后续讨论 2

顺着彼此的依据读下去
  1. 固定链接

    Weak capture fixes the ticks. The controller's deinit runs and invalidate() there stops the timer, so nothing fires after the screen closes.

    The thread is the part nobody has looked at. deinit runs wherever the last strong reference goes away, and invalidate() is supposed to be called on the thread whose run loop holds the timer. A controller that only ever gets released on the main thread works, but that's luck in the setup rather than anything the code promises. The trouble would only show up when something on another thread drops the last reference.

  2. 固定链接

    Worth noting that the same thing happens when the timer turns into a Task loop around Task.sleep, and it's easier to miss. An unstructured task has no parent, so nothing cancels it when the controller goes away. If the closure holds self strongly, the loop keeps the controller alive and deinit never gets the chance to cancel it. [weak self] only helps if the strong reference is taken inside each iteration. A guard let self above the loop holds it for the whole loop. In Kotlin a coroutine launched in a scope is cancelled with that scope, which is the part I keep missing on the Swift side. Is there anything that ties an unstructured Task to an object's lifetime, short of keeping the handle and calling cancel() in deinit?

同一个问题的其他角度

  1. Assigning realloc back to the only pointer leaks on failure

    *buffer = realloc(*buffer, n) turns up in a lot of growth helpers, and it is wrong in a specific way. If the allocation fails…

    回复 8最近活动 clua
  2. What survives a catalog reload?

    A slice is an address and a length. Neither field keeps the backing allocation alive, and the Zig reference is plain that poi…

    回复 15最近活动 zigswiftrustcppodin
  3. The dangling pointer warning went away, the pointer did not

    Passing a name into a C++ library through a wrapper, and the first version was one line, CString::new(name).unwrap().as_ptr()…

    回复 3最近活动 rustcpp