Retain Cycles and Capture Lists in Swift
A retain cycle is a memory leak where two objects hold strong references to each other, preventing deallocation. This often happens in closures that capture `self`, like network callbacks.
WHY IT EXISTS Swift manages memory with Automatic Reference Counting instead of a garbage collector. Every object tracks how many strong references point to it, and ARC deallocates it the instant that count hits zero. That breaks down when two objects hold strong references to each other: neither count ever reaches zero, so neither object is ever freed. Capture lists exist because ARC needs a way to say a given reference should not keep the other object alive.
THE MENTAL MODEL Picture two people each holding the other's hand so tightly that neither can leave the room, even after the party is over. Object A strongly references object B, and B strongly references A back. ARC only lets go when nobody is holding on, so this pair stays in memory forever, leaking. A capture list is the instruction you give a closure about how tightly to hold the objects it references: strongly by default, weakly, or unowned.
HOW IT WORKS The classic case is a class instance that stores a closure as a property, such as a network completion handler, and that closure captures self to read or write the instance's properties. The instance strongly retains the closure, and the closure strongly retains self, forming a cycle. Writing weak self in the capture list makes that reference weak: it does not increase self's retain count and becomes nil automatically once self is deallocated, so the closure body typically unwraps it with self question mark or a guard let. Unowned self skips that unwrapping, assuming self can never be nil, at the cost of a crash if that assumption is wrong.
WHEN IT MATTERS This matters anywhere a closure is stored and outlives the function that created it: completion handlers, delegate closures, timers, notification observers. It does not matter for closures like map or filter that run and finish within the same call. The footgun is that a leaked view controller does not crash your app, it just quietly stays in memory, so retain cycles often go unnoticed until Instruments shows memory climbing on every screen visit.
ONE CONCRETE EXAMPLE A ProfileViewController holds a strong reference to an ApiClient, and passes it a completion closure that sets self dot username to data dot name. If that closure captures self strongly, the ApiClient and the view controller keep each other alive even after the user navigates away. Changing the closure to weak self and unwrapping with self question mark lets the view controller deallocate normally once the screen is dismissed.
Read the original → docs.swift.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.