How do you find a retain cycle with the Memory Graph debugger?

3.7.2 · L3 · Interview

Predict first

After opening and closing a screen five times, the memory graph lists five instances of its view model. What does that tell you?

// A: normal, they are cached
// B: it is not being freed, so look at what holds it
// C: the debugger is wrong
  • A
  • B
  • C

Mental model. The memory graph is a floor plan with arrows showing who is holding whose hand. A loop of hands that nobody outside is holding is the leak.

Reproduce the problem first: open and close the suspect screen a few times. Then pause and choose Debug Memory Graph in Xcode. The navigator lists every live object by type: search for your view controller or view model. If it shows more instances than screens that are open, it was not freed. Select one and the canvas draws what holds it; a purple exclamation mark flags objects Xcode believes are leaked. The reference edges are labelled by kind (strong, weak, unowned), so you can read the cycle off the picture and see which link to weaken.

Turn on Malloc Stack Logging in the scheme so that selecting an object shows the backtrace of where it was allocated. Combine this with a deinit log in the suspect type, and with the command-line tools leaks, heap and vmmap when you work outside Xcode. For long-running trends, switch to Instruments (Allocations), since a single snapshot only tells you what is alive now.

The screen is closed, so no instance should remain. Five means every visit left one behind: select one and read what still references it.

Common traps

  • Looking after only one visit to the screen. A single instance may be legitimate. Repeat the action and compare counts.
  • Trusting the absence of a purple mark. Abandoned memory is reachable and is not flagged. Check instance counts yourself.

They might follow up with

  • How do you filter the noise? Filter by your module and show only leaked blocks, then follow one chain.
  • What if the cycle goes through a closure? The edge is labelled as the closure’s captured variable, which points to the capture list to change.

Related questions

  • 3.3.1 What is a retain cycle, and how do you find and fix one?
  • 3.7.1 What is the difference between a leak, abandoned memory, fragmentation and a memory spike?
  • 3.7.3 How do you use Instruments’ Allocations, Leaks and VM Tracker to track a memory problem?

← What is the difference between a leak, abandoned memory…   How do you use Instruments’ Allocations, Leaks and VM Tracker to… →