What happens to an object’s memory while unowned references to it still exist?

3.2.5 · L5 · Learning

Predict first

What does this print?

final class A { deinit { print("deinit") } }
var a: A? = A()
unowned let u = a!
a = nil
print("after")
  • after, deinit
  • deinit, after
  • Crash

Mental model. When a tenant moves out, the flat is stripped but the building stays up until the last visitor with a key has handed it in.

Every Swift object has a strong count and a separate unowned count. When the strong count reaches zero, deinit runs and the stored properties are destroyed: the object is logically dead. But its memory is not freed until the unowned count also reaches zero. The dead husk stays allocated so that an unowned read can check the state and trap with a clear message, instead of reading memory that has been reused.

So deinit runs on time even when unowned references exist, and reading one afterwards is a deterministic trap, not a random crash. The cost is that a large object referenced by a stray unowned pointer keeps its full allocation until the holder goes away. Weak references behave similarly through a side table that outlives the object. This is why unowned is cheap and safe at once, and why it is only correct when the holder really cannot read it late.

The unowned reference does not delay deinit: the object dies when the last strong reference goes. Nothing reads u, so there is no trap.

Common traps

  • “deinit is delayed by unowned references.” It is not. Only the memory block is kept, never the destruction.
  • Assuming unowned reads are undefined. They are defined: they trap. Undefined is the unsafe variant.

They might follow up with

  • Why keep the memory at all? So an unowned load can inspect the header safely. Without it the check would read freed memory.
  • What does the trap say? Something like “attempted to read an unowned reference but object was already deallocated”.

Related questions

  • 3.2.4 What is unowned(unsafe), and when would you ever use it?
  • 3.4.4 What is a side table, and when does the runtime create one?
  • 1.5.6 unowned var view = UIView() then print(view) — what happens?

← What is unowned(unsafe), and when would you ever use it?