Why is a weak reference optional, what can be weak, and what is the trap when reading one?

3.2.2 · L2 · Interview

Predict first

What does this print?

final class Node {}
weak var w = Node()
print(w as Any)
  • Optional(Node)
  • nil

Predict first

Does this compile in a recent Swift?

final class Owner { weak let parent: Owner? = nil }
  • Yes
  • No

Mental model. A weak reference is a sticky note that blanks itself when the thing it names disappears. Copy what it says before you act on it.

The runtime zeroes a weak reference when its target dies, which is why the type must be optional: nil is the only value that means “gone”. Only class types (and class-bound protocols) can be weak, because only they have the identity ARC tracks; structs, enums and tuples cannot. For a long time a weak property also had to be var because the runtime writes to it; newer Swift versions allow weak let.

The trap is reading it twice. Each read of self.delegate can see a different answer, because the object may die between them. if delegate != nil { delegate!.go() } can crash. Copy it to a strong local once, guard let delegate else { return }, then use the local for the rest of the scope: that local keeps it alive while you work. A weak reference to an object that is created and dropped on the same line is nil immediately.

The new Node has no strong owner, so it is deallocated before the next line, and the weak reference reads nil.

Newer Swift versions accept weak let. Older ones required weak var because the runtime mutates the reference.

Common traps

  • Check-then-use on a weak property. The object can die between the check and the use. Bind it to a local first.
  • Assigning a fresh object to a weak variable. Nothing else holds it, so it is gone at once and the compiler warns you.

They might follow up with

  • Why can a weak value be read from several threads? The read is an atomic load followed by a retain, so you get either a live object or nil.
  • Can a weak reference keep the object’s memory? No. It holds a side-table entry that remains until the weak reference itself is gone; see the side-table question.

Related questions

  • 3.2.1 What is the difference between strong, weak, unowned and unowned(unsafe)?
  • 3.3.2 Why should a delegate property be weak, and why must the protocol be class-bound?
  • 3.4.4 What is a side table, and when does the runtime create one?

← What is the difference between strong, weak, unowned and…   How do you decide between weak, unowned and strong for a… →