Memory Management with ARC
TrailLog's hike detail screen closes, but its memory never comes back. Open and close it twenty times and the app is using far more memory than it should. Nothing crashed; objects simply never got freed. Understanding how iOS frees memory explains why.
The rule
ARC frees an object when its last strong reference disappears.
Swift uses Automatic Reference Counting (ARC) for class instances. Every strong reference to an object adds one to its count; when a reference goes away, the count drops. At zero, the object is destroyed and deinit runs. No garbage collector pauses the app; the counting happens as your code runs.
ARC applies to classes (and closures). Structs and enums are values and are simply copied, so they are not counted.
The problem is a retain cycle: two objects holding strong references to each other. Neither count ever reaches zero, so both leak.
class HikeDetailViewController: UIViewController {
var onSave: (() -> Void)?
var hike: Hike?
override func viewDidLoad() {
super.viewDidLoad()
// Leak: the controller owns the closure, and the closure owns the controller.
onSave = { self.showSavedBanner() }
}
}The fix is to make one side weak or unowned:
onSave = { [weak self] in
self?.showSavedBanner()
}weak— does not add to the count. It must be an optionalvar, and becomesnilautomatically when the object is destroyed. Safe default.unowned— also does not add to the count, but is not optional. Use it only when the other object is guaranteed to outlive this reference; otherwise accessing it crashes.
The classic places cycles hide: closures stored as properties that mention self, and delegates, which is why delegate properties are almost always declared weak var delegate.
The shortcut
Draw an arrow for each strong reference. If the arrows make a loop, break one arrow with weak. Usually the arrow pointing "back up" to the owner is the one to weaken.
Draw the owner's arrow
The detail screen stores
onSave: screen → closure, strong.
The trap is adding [weak self] everywhere by habit and never checking. A closure that is not stored — such as the one passed to UIView.animate — runs and is released, so it creates no cycle.
Drills
Answer fast — trust the rule.
When does ARC destroy a class instance?
Which of these is managed by ARC?
What happens to a weak reference when the object it points to is destroyed?
Why are delegate properties usually declared weak var delegate?
Remember
- ARC frees an object when its last strong reference disappears; it only counts class instances and closures.
- Shortcut: draw the strong arrows; a loop is a leak, so weaken the back arrow.
weakbecomesnilsafely;unownedcrashes if the object is gone.
