How does a weak reference get zeroed when the object dies?

3.4.5 · L5 · Learning

Predict first

What does reading a weak reference do for a Swift object?

// A: copies the raw pointer
// B: atomically checks the object is alive and retains it
// C: locks the whole app while it looks
  • A
  • B
  • C

Mental model. Swift’s version is an office that stamps “gone” on a single card everyone checks. The Objective-C version keeps a phone book of everyone to call when the object leaves.

Two mechanisms exist, depending on the object. For native Swift objects the side table (see above) records the weak count and the object’s state. A weak variable holds a pointer to that side table, not the object. Reading it checks whether the object is still alive, and if so retains it, in one atomic step. When the object deinitialises it marks itself dead in the table, and every weak reader then sees nil lazily, with no need to find and rewrite each variable.

For Objective-C-compatible objects (NSObject subclasses) the Objective-C runtime keeps a global weak table mapping each object to the addresses of all weak variables pointing at it. objc_storeWeak registers a variable, and dealloc walks the list and sets each one to nil. That table is shared and locked, so heavy weak use on Objective-C objects can contend, and deallocation cost grows with the number of weak references.

The read returns a strong, retained object if it is still alive, and nil otherwise, in one atomic step. That is why you get a safe optional.

Common traps

  • Thousands of weak references to one NSObject. Its deallocation has to visit every one.
  • Assuming the two schemes behave identically in cost. Measure on the real object type.

They might follow up with

  • Is reading a weak variable thread-safe? Yes: it either returns a live, retained object or nil.
  • What happens if a weak reference is read during deinit? The object is already marked as deinitialising, so you get nil.

Related questions

  • 3.4.4 What is a side table, and when does the runtime create one?
  • 3.2.2 Why is a weak reference optional, what can be weak, and what is the trap when reading one?

← What is a side table, and when does the runtime create one?   What is a tagged pointer, and what other tricks avoid allocating? →