Retain Cycles: When Strong References Point at Each Other
Understand precisely why two objects holding strong references to each other can never reach a retain count of zero, recognize the realistic patterns that create this situation, and learn to fix it by making one side of the relationship weak.
Every memory-management rule covered so far in this module has rested on one idea: an object is deallocated once nothing holds a strong reference to it anymore. That rule is simple and reliable for a single reference, or even a long chain of references, as long as the chain never loops back on itself. But real object graphs are rarely a straight line — a parent object often needs to keep a reference to its children, and a child object often needs to keep a reference back to its parent, and the moment both of those references are declared strong, the reference-counting system that has protected you throughout this course quietly stops working. This lesson is about that failure mode — the retain cycle — because it is, without exaggeration, the single most common source of memory leaks in real Objective-C codebases, and understanding it precisely is what separates a programmer who merely knows ARC's rules from one who can actually design a memory-safe object graph.
Why a Cycle of Strong References Never Reaches Zero
Recall the core mechanic of ARC: every object carries a retain count, incremented each time a strong reference is made to point at it and decremented each time such a reference is cleared or reassigned. When that count reaches zero, ARC deallocates the object. This works flawlessly for a tree-shaped or line-shaped object graph, because there is always some path by which the last strong reference eventually gets cleared, the count eventually hits zero, and deallocation proceeds.
Now consider two objects, Parent and Child, where Parent holds a strong reference to Child and Child also holds a strong reference back to Parent. Suppose the rest of the program drops every other reference it holds to both objects — no local variable, no other object, nothing outside this pair points at either one anymore. Intuitively, both objects are garbage: nothing in the reachable part of the program can ever use them again. But look at their retain counts. Parent's retain count is still 1, because Child is still strongly holding it. Child's retain count is still 1, because Parent is still strongly holding it. Neither count is zero, so ARC will not deallocate either object.
This is the exact mechanism of a retain cycle, and it is worth stating with complete precision, because the reasoning is subtle: deallocating Parent would require first clearing Child's strong reference to it, but Child cannot be deallocated — and its ivars cannot be torn down — until Child itself has a retain count of zero, which requires Parent's strong reference to Child to be cleared first, which only happens when Parent is deallocated. Each object's deallocation is blocked on the other object's deallocation happening first. Neither can go first, so neither ever happens. The two objects keep each other alive forever, orphaned from the rest of the program, silently consuming memory for the life of the process.
• A retain count only reaches zero when every strong reference pointing at that object has been cleared. • In a two-object strong cycle, each object is the thing keeping the other object's retain count above zero. • The result is a leak, not a crash: the objects are unreachable from the rest of the program but never deallocated.
The Spec Diagram: Parent and Child Holding Each Other
The canonical shape of this problem is drawn as a small diagram, reproduced exactly below, and it is worth studying until the picture and the retain-count reasoning above feel like the same fact stated two ways. Parent points down at Child with a strong arrow; Child points back up at Parent, also with a strong arrow. Two arrows, both strong, forming a closed loop between exactly two objects.
Notice what makes this a cycle and not just two references: the arrows point in a loop. A Parent that strongly owns many Child objects, none of which reference Parent back, is not a cycle at all — it's an ordinary tree, and it deallocates correctly once nothing outside holds the parent. The danger appears specifically when the arrow direction closes a loop, however many objects are involved — the cycle can just as easily run through three, four, or a dozen objects handing strong references around in a ring, but the two-object parent/child shape is the version you will encounter constantly, because so many real designs naturally want a child to know its parent.
Realistic Scenario: Parent-Child Object Graphs
The most literal version of this problem shows up whenever you model a hierarchy and give the child object a convenience reference back to its owner. A Document object holds a strong array of Page objects it owns; each Page keeps a reference back to its document so it can, say, ask the document for its default font or notify it that the page changed. If that back-reference is declared strong, you have built the exact cycle in the diagram: the Document strongly retains every Page in its array, and every Page strongly retains the Document right back. Close the last window, release your last strong reference to the Document from the view controller that created it — the whole graph should vanish, but it doesn't, because Document and each Page are still holding each other up.
This pattern recurs everywhere a composite object is built from parts that need to reach back to the whole: a TableView and its Cells, a Node and its children in a tree with a parent pointer, a Session and the Request objects it manages. In every one of these designs, exactly one direction of the relationship represents true ownership — the Document genuinely owns its Pages; a Page does not own the Document it happens to belong to — and only the ownership direction should ever be strong.
Realistic Scenario: Delegate Patterns Without a Weak Delegate
The second classic source of retain cycles is the delegate pattern, and it deserves special attention because it was, historically, one of the most common beginner mistakes in Objective-C code. A NetworkFetcher object is given a delegate so it can report back when a download finishes; a ViewController creates the NetworkFetcher, keeps a strong reference to it in a property, and sets itself as the fetcher's delegate: fetcher.delegate = self;. If NetworkFetcher's delegate property is declared strong — @property (nonatomic, strong) id<NetworkFetcherDelegate> delegate; — you now have ViewController strongly retaining NetworkFetcher through its fetcher property, and NetworkFetcher strongly retaining ViewController right back through its delegate property. The exact same Parent/Child shape, just with different class names.
This is precisely why the idiomatic Objective-C convention — one you should treat as close to a hard rule — is that a delegate property is declared weak, never strong: @property (nonatomic, weak) id<NetworkFetcherDelegate> delegate;. The delegate relationship is a classic case where the reference does not represent ownership at all; the NetworkFetcher does not own its delegate, it merely knows how to reach it to report events, and the object that does own the relationship (the ViewController, through its strong fetcher property) is on the other side.
Realistic Scenario: Blocks That Capture self Strongly
The Blocks lesson earlier in this module covered how a block automatically captures, by strong reference, every Objective-C object it uses from its enclosing scope — including self, if the block's body refers to self or to any instance variable or property accessed implicitly through self. That capture behavior is exactly what creates a retain cycle the moment an object stores such a block in one of its own properties. Picture a self.completionHandler = ^{ [self doSomething]; }; assignment inside a method: the block captures self strongly, and self is strongly holding the block through its completionHandler property. The object and its own stored block are now retaining each other, which is the same cyclic shape as Parent and Child, just expressed through a block instead of a second named object.
This is worth calling out explicitly because it is easy to miss: nothing about the syntax of a block capture looks like the @property (nonatomic, strong) declarations that make the parent/child and delegate examples visually obvious. The strong capture happens implicitly, inside the block literal's braces, simply because the block's body mentions self. Recognizing this pattern — any block stored as a property or ivar that also references self inside its body — is one of the most valuable retain-cycle instincts to build, and it is covered in full depth, including the __weak typeof(self) weakSelf fix, in the Blocks lesson; this lesson cross-references it here only to place it correctly among the other realistic causes of a strong reference cycle.
The Fix: Make One Side of the Relationship Weak
The fix for every example above follows the same shape: leave one direction of the strong/strong pair exactly as it is, and change the other direction from strong to weak. A weak reference points at an object without incrementing its retain count and without keeping it alive, and critically, it is automatically set to nil by ARC the moment the object it points at is deallocated — so a weak reference can never leave you holding a dangling pointer to freed memory. Once one side of a cycle is weak, the loop is broken: that object no longer contributes to the other object's retain count, so when every strong reference into the pair is finally cleared from outside, the retain count of the strongly-held object can genuinely reach zero, deallocate normally, and its deallocation then clears the weak reference that was pointing at it.
Applying this to the Document/Page example: Document keeps its @property (nonatomic, strong) NSArray<Page *> *pages;, unchanged, because Document really does own its pages. Page's back-reference becomes @property (nonatomic, weak) Document *document;. Now, when the Document is released from outside, its retain count reaches zero, it deallocates, its pages array is torn down, each Page's retain count then reaches zero because the only strong reference to it was from that array, and each Page deallocates too — with its now-meaningless document pointer having already become nil automatically.
Deciding Which Side Should Be Weak: Follow Ownership Direction
The single question that resolves every one of these cases correctly is: which object, conceptually, owns the other? Ownership is directional even when a reference relationship looks symmetric on the page. A Document owning its Pages is a clear, real ownership relationship — if the document goes away, there is no reason for any of its pages to keep existing independently. A Page knowing which document it belongs to is not ownership in that sense at all; it is closer to a label, a piece of information the page needs to do its job, not a claim that the page is responsible for keeping the document alive.
The rule that falls out of this is simple to state and worth memorizing verbatim: the strong reference should always point in the direction of ownership — from the owner to the thing it owns — and any reference that runs the other way, from the owned object back up to its owner, should be weak. This is exactly why delegate properties are conventionally weak (the object being delegated to does not own the delegate; whoever created the relationship, usually a view controller, already owns both ends through some other strong path) and exactly why a child's back-pointer to its parent is conventionally weak. When you are unsure which side to mark weak in a new design, ask which object's lifetime should control the other's — the answer to that question tells you where the strong arrow belongs, and the weak arrow goes on the reverse edge.
• Owner → owned: strong. Owned → owner (or observer → subject, or delegate-holder → delegate): weak. • A weak reference does not increment retain count and is automatically nilled out when its target deallocates. • When two objects reference each other and you aren't sure which side to weaken, ask which one's lifetime should control the other's.
Declaring a Weak Back-Reference
The strong side is the direction of true ownership: Document owns its Page objects. The weak side is the reverse edge: a Page's reference back to the Document it belongs to.
@interface Page : NSObject
@property (nonatomic, weak) Document *document; // back-reference: weak
@end
@interface Document : NSObject
@property (nonatomic, strong) NSArray<Page *> *pages; // ownership: strong
@endCode Examples
Document.pages strongly retains every Page; each Page.document strongly retains the Document right back. Once the caller that created the Document drops its own strong reference, both objects still have a retain count of 1 from each other, so neither is ever deallocated — a leak, even though nothing outside can reach either object anymore.
@property (nonatomic, strong) Document *document; // PROBLEM: strong back-reference
Common mistake: Declaring a child's reference back to its owning parent as strong instead of weak.
Only Document.document was changed, from strong to weak. Document still strongly owns its pages array exactly as before — that direction is genuine ownership — but a Page no longer keeps its Document alive. When the caller releases the Document, its retain count reaches zero, it deallocates, its pages array is torn down, and each Page deallocates in turn because nothing strong points at it anymore.
@property (nonatomic, weak) Document *document; // FIXED: weak back-reference
ViewController strongly owns fetcher through its own property, which is the real ownership relationship. fetcher.delegate pointing back at the ViewController is not ownership — the fetcher is just told where to report — so it must be weak. This is why delegate properties are, by nearly universal Objective-C convention, declared weak.
@property (nonatomic, weak) id<NetworkFetcherDelegate> delegate; // correct self.completionHandler strongly holds the block, and the block's body references self, which the compiler captures strongly by default — so self and its own stored block now retain each other. See the Blocks lesson for the full __weak typeof(self) weakSelf fix; this example exists here only to show the block-capture cycle alongside the object-reference cycles above.
Common mistake: Storing a block in a property while the block's body references self, without weakening the capture.
@interface Document : NSObject
@property (nonatomic, strong) NSMutableArray<Page *> *pages;
@end
@interface Page : NSObject
@property (nonatomic, strong) Document *document; // PROBLEM: strong back-reference
@end
@implementation Document
- (void)addPageWithText:(NSString *)text {
Page *page = [[Page alloc] init];
page.document = self; // Page now strongly retains Document
[self.pages addObject:page]; // Document strongly retains Page
}
@end@interface Page : NSObject
@property (nonatomic, weak) Document *document; // FIXED: weak back-reference
@end
@implementation Document
- (void)addPageWithText:(NSString *)text {
Page *page = [[Page alloc] init];
page.document = self; // weak: does not retain Document
[self.pages addObject:page]; // Document still strongly owns its pages
}
@end// Cycle:
@interface NetworkFetcher : NSObject
@property (nonatomic, strong) id<NetworkFetcherDelegate> delegate; // PROBLEM
@end
// Fix:
@interface NetworkFetcher : NSObject
@property (nonatomic, weak) id<NetworkFetcherDelegate> delegate; // correct
@end
@implementation ViewController
- (void)startFetch {
self.fetcher = [[NetworkFetcher alloc] init]; // ViewController owns fetcher: strong
self.fetcher.delegate = self; // fetcher does not own ViewController: weak
}
@end@interface Downloader : NSObject
@property (nonatomic, copy) void (^completionHandler)(void);
@end
@implementation Downloader
- (void)configure {
self.completionHandler = ^{
[self logCompletion]; // captures self strongly -> cycle with the property above
};
}
@endA Strong Reference Cycle
Parent strongly retains Child; Child strongly retains Parent right back. Neither retain count can reach zero, because each object's deallocation is blocked on the other's.
Parent
│ strong
↓
Child
│ strong
└────────→ ParentWhy Retain Cycles Are the Leading Cause of Leaks in Shipping Apps
Ask any experienced Objective-C or Cocoa developer what the most common category of memory leak is in a real production codebase, and the answer is almost always some variant of a retain cycle — not a forgotten manual release, not a buffer overrun, but two or more objects quietly holding each other up long after the rest of the app has moved on. It is a particularly dangerous kind of bug precisely because it doesn't crash: the app keeps running, the leaked objects sit in memory doing nothing useful, and the only symptom is memory usage that creeps upward the longer the app runs, often only surfacing weeks later as user complaints about sluggishness or the app being killed by the operating system for using too much memory. This is why tools like Xcode's Memory Graph Debugger and the Instruments Leaks template exist specifically to visualize object graphs and highlight cycles, and why disciplined use of weak references for every back-pointer, delegate, and self-capturing block is treated as a baseline professional habit rather than an optional optimization.
@property (nonatomic, strong) Document *document; // a Page's back-reference to its owning Document
@property (nonatomic, weak) Document *document;
A strong back-reference from an owned object to its owner recreates the Parent/Child cycle exactly. The owner should hold the owned object strongly; the reverse edge must be weak so the owner can still be deallocated when nothing else references it.
@property (nonatomic, strong) id<SomeDelegate> delegate;
@property (nonatomic, weak) id<SomeDelegate> delegate;
A strong delegate property, paired with the common pattern of the delegate's own object strongly owning the delegating object, is one of the most frequent real-world retain cycles. Delegate properties are conventionally weak precisely to avoid this.
self.handler = ^{ [self doWork]; }; // block stored on self, capturing self strongly, never weakened
__weak typeof(self) weakSelf = self; self.handler = ^{ [weakSelf doWork]; };
A block stored as a property on an object, whose body references self, strongly captures self by default — creating a cycle between the object and the block it owns. Weakening the captured reference to self breaks the cycle, as covered in depth in the Blocks lesson.
Why does a two-object retain cycle prevent both objects from ever being deallocated? A. ARC detects the cycle and refuses to run at all until it is fixed B. Each object's retain count can never reach zero because the other object still holds a strong reference to it C. The compiler treats cyclic references as a syntax error D. Retain cycles only occur with manual reference counting, never under ARC
B. Each object's retain count can never reach zero because the other object still holds a strong reference to it — Deallocation requires a retain count of zero. In a cycle, Parent is kept above zero by Child's strong reference, and Child is kept above zero by Parent's strong reference, so neither ever reaches zero and neither is ever deallocated.
In the spec's Parent/Child diagram, what breaks the cycle? A. Making Parent's reference to Child weak instead of strong, since Parent owns Child B. Deleting the Child object manually C. Making Child's reference back to Parent weak instead of strong, since Child does not own Parent D. Adding a second strong reference from an outside object
C. Making Child's reference back to Parent weak instead of strong, since Child does not own Parent — Parent's reference to Child represents true ownership and should stay strong. Child's back-reference to Parent is not ownership, so weakening it breaks the cycle while preserving normal ownership semantics.
Why are delegate properties conventionally declared weak in Objective-C? A. Weak properties are faster to access at runtime B. The delegating object usually does not own its delegate, and the delegate's owner often already strongly owns the delegating object, so a strong delegate property would create a cycle C. Delegates must be weak or the compiler refuses to compile the protocol D. Weak delegate properties are automatically thread-safe
B. The delegating object usually does not own its delegate, and the delegate's owner often already strongly owns the delegating object, so a strong delegate property would create a cycle — A strong delegate property paired with the very common pattern of the delegate's owner also strongly owning the delegating object recreates the Parent/Child cycle. Declaring delegate properties weak is the standard fix.
How can a block stored in a property create a retain cycle even without a second named object involved? A. Blocks are always leaked regardless of what they capture B. If the block's body references self and the block is stored on a property of self, the block strongly captures self while self strongly holds the block C. Blocks cannot be stored in properties at all under ARC D. The cycle only happens if the block is declared with the strong attribute explicitly
B. If the block's body references self and the block is stored on a property of self, the block strongly captures self while self strongly holds the block — A block automatically captures any Objective-C object it references, including self, by strong reference by default. If that block is then stored in a strong property on self, self and the block now retain each other.
What general rule determines which side of a two-way object relationship should be declared weak? A. The side that was declared second in the source file B. Whichever property has a shorter name C. The strong reference should point in the direction of ownership; the reverse reference, from owned object back to owner, should be weak D. Both sides should always be strong, and cycles should be prevented by never creating back-references at all
C. The strong reference should point in the direction of ownership; the reverse reference, from owned object back to owner, should be weak — Ownership is directional: the owner should strongly retain what it owns, while any reference running the opposite way — from the owned object back to its owner — should be weak so it does not keep the owner alive artificially.
A Team class has a strong NSMutableArray<Player *> *players property. A Player class has a strong Team *team property so each player can look up its team. Identify the retain cycle, state which property should change and to what, and explain why that direction is the correct one to weaken.
Team genuinely owns its Players (if the Team goes away, its Players array should be released), so Team.players stays strong. Player.team is a back-reference, not ownership, so it must change to @property (nonatomic, weak) Team *team;. With that change, releasing the last strong reference to a Team lets its retain count reach zero, its players array deallocates, and each Player then deallocates too since nothing strong points at it — the cycle is broken.
Ask which object owns the other: does a Team own its Players, or does a Player own the Team it belongs to?