In the closures article, when we covered breaking retain cycles with [weak self], we left one question open. There is also unowned instead of weak—what is the difference, and when should you use it? To answer, we first need to look beneath the surface at how ARC actually works.
This is part 1 of the Swift intermediate series. We cover how ARC works, the exact differences among strong, weak, and unowned references, and practical criteria for choosing “weak vs unowned.” The history of moving from Objective-C MRC (Manual Reference Counting) to ARC is covered separately; here, we focus on the present from Swift’s perspective.
What ARC Really Is — The Compiler’s Ledger, Not a Runtime Janitor
Many people think of ARC (Automatic Reference Counting) as “Swift’s garbage collector,” but the underlying mechanism is fundamentally different.
Garbage collection (GC) periodically runs a separate runtime system that finds and cleans up “objects nobody is using.” ARC, by contrast, is decided at compile time. The compiler analyzes code flow and inserts retain (count +1) calls where references are created and release (count -1) calls where references end. As soon as the count reaches 0, deinit is called and the memory is freed.
This difference produces two important properties. First, deallocation is deterministic. Instead of “the GC will clean it up sometime,” the object is freed on the exact line where the last reference disappears. That is why you can entrust resource cleanup to deinit. Second, there is no cleanup phase that pauses the program. It is a real example of “not sacrificing performance for safety,” as discussed in part 1 of the philosophy series.
It is not free. Reference-count changes must be thread-safe, so they use atomic operations; this is the hidden cost of reference types. That is one reason Swift favors structs by default: value types have no such ledger to maintain. Crucially, ARC cannot break cycles. GC can find groups of objects unreachable from the root and clean them up even when they form a cycle, but with ARC, counts cannot reach 0 while objects continue counting each other. That is the structural reason retain cycles remain the programmer’s responsibility.
Three Kinds of References — The Language of Ownership
In the world of ARC, references fall into three types depending on whether “I am responsible for keeping this object alive.”
strong (the default) means ownership. It increments the count, and the object stays alive as long as I hold it. Every reference declared without an attribute is strong.
weak is non-owning observation. It does not increment the count, so the object is deallocated when all remaining strong references disappear. At that moment, the runtime automatically changes the weak reference to nil. Therefore, a weak variable must be optional and var. The fact that it can become nil at any time is encoded in the type, as discussed in the optionals article.
unowned is also non-owning, but it gives up nil handling. Like weak, it does not increment the count, but accessing it after the target is deallocated crashes immediately instead of returning nil. In return, it is non-optional and can be used without unwrapping.
In short: strong means “I will keep you alive,” weak means “I know you may disappear,” and unowned means “I am certain you will outlive me.”
weak vs unowned — The Criterion Is the Lifetime Relationship
So when should you use weak and when unowned? The criterion is not syntax but the lifetime relationship between the two objects.
Use weak when the other object may disappear before you. Delegates are the textbook example. When a view references its delegate, usually a view controller, it is perfectly normal for the view controller to be deallocated first. That is why delegate properties are conventionally weak var delegate, and optional chaining (delegate?.didFinish()) expresses “ignore it if absent” when they are used. Nil is a normal state in this relationship.
Use unowned when the other object is structurally guaranteed to live as long as or longer than you. The textbook example is a credit card and its customer. A card cannot exist without its customer, and the customer must remain alive while the card is alive. unowned let customer is the exact expression. Because it is non-optional, there is no repeated unwrapping noise, and it can be declared with let to preserve immutability.
If we compare this criterion with the criterion for forced unwrapping !, the consistency becomes clear. unowned is the reference version of !. It declares, “If this is nil here, my design is broken,” and when that certainty is wrong, it reports the problem immediately with a crash instead of failing silently. If you are not certain, use weak. In practice, the prevailing rule is “when in doubt, weak.” The costs of weak—optional handling and slight runtime overhead—are cheaper than the crash risk. Treat unowned as a tool to use sparingly, only where the lifetime guarantee is obvious from the code structure.
The same criterion applies to closure captures. [weak self] is the default choice because, in most cases, there is no guarantee that self will still be alive when the closure runs. On the other hand, unowned is justified when the lifetimes of the closure and self are tied—for example, when capturing self in an immediately executed closure for a lazy property. If the property is alive, self is alive.
Verify with Tools — Instrumentation, Not Intuition
Retain cycles cannot all be caught through code review. Knowing three verification tools turns “it should probably be fine” into “I checked.”
deinit logs are the cheapest tool. If you close a screen and the view model’s deinit is not logged, there is a leak somewhere. Simply getting into the habit of adding one print statement to suspicious classes during development catches most cycles early.
Xcode’s Memory Graph Debugger displays cycles visually. Press the memory graph button in the debug bar while running to see current heap objects and reference relationships as a graph. If an object that should have been deallocated remains, you can follow the arrows to find what is holding it. Objects suspected of leaking are also marked with purple exclamation points, which is another clue.
The Leaks template in Instruments measures memory over time. Repeatedly open and close a screen and watch whether memory grows in steps; this makes it suitable for regular checks before release.
For reference, the usual suspects in cycles are predictable: closures stored in properties (the closures article), accidentally declaring a delegate as strong, and failing to unregister NotificationCenter or timer-related registrations (the NSTimer article). Checking these three areas when reviewing new code covers most cycles.
Summary
- ARC inserts retain/release at compile time; it is not a runtime janitor. Deallocation is deterministic and there are no GC pauses, but ARC cannot resolve cycles by itself.
- References are the language of ownership. strong owns, weak is non-owning with awareness that nil is possible, and unowned is non-owning with confidence that the other object lives longer.
- The criterion is the lifetime relationship. If the other object may disappear first, use weak; if it is structurally guaranteed to live longer, use unowned; when uncertain, use weak.
- unowned is the forced-unwrapping version of a reference. Use it only where the certainty is evident in the code.
- Use deinit logs, Memory Graph Debugger, and Instruments Leaks to verify through instrumentation rather than intuition.
The next article is part 2 of the intermediate series: generics. We will cover how the T inside angle brackets achieves type safety and code reuse at once, and when a where clause is necessary.

![Cover image for [Swift Intermediate #1] Swift ARC Explained: Choose weak vs unowned by Lifetime](/assets/images/posts/dea74de0-7d82-4c90-b8e1-e7be2bdaf005/1.jpg)