Using __weak to Break Retain Cycles in Objective-C ARC
Learn how to use __weak delegates and the weak-strong block pattern to eliminate retain cycles in Objective-C ARC while keeping APIs explicit about object lifetime.
10 Jan 2026, 06:27 UTC

The problem: a view controller that never disappears
When you push a view controller onto a navigation stack and then pop it, you expect the instance to be deallocated. With ARC enabled, you might still see the object linger because a block or delegate holds a strong reference back to self. This creates a retain cycle that ARC cannot break on its own.
Thesis: ARC makes ownership explicit, not automatic
ARC inserts the correct retain and release calls at compile time, but it does not eliminate the need to think about ownership. The engineer must decide which qualifier—strong, weak, unsafe_unretained, or copy—best expresses the intended lifetime of each reference.
Why __weak is the default for back-references
A __weak reference does not keep the object alive. On runtimes that support zeroing weak references (iOS 5+, macOS 10.7+, and later), the reference automatically becomes nil when the referenced object is deallocated, turning a potential dangling pointer into a safe nil check. This makes __weak ideal for delegates, data sources, and any parent-child back link.
Block capture and the weak-strong dance
Blocks capture Objective-C objects strongly by default. If a block stored in a property captures self strongly, you recreate the retain cycle. The common pattern is:
- Declare a weak version outside the block:
__weak typeof(self) weakSelf = self;
- Inside the block, promote to a strong reference for the duration of the work:
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
// use strongSelf safely
}
This guarantees that if self is deallocated before the block runs, the block simply does nothing instead of crashing.
Worked example: a DownloadClient with a weak delegate and a completion block
Consider a simple networking helper that reports progress to a delegate and notifies the caller via a completion block.
@interface DownloadClient : NSObject
@property (nonatomic, weak) id<DownloadClientDelegate> delegate;
@property (nonatomic, copy) void (^completion)(NSData *data, NSError *error);
- (void)startDownloadWithURL:(NSURL *)url;
@end
@implementation DownloadClient
- (void)startDownloadWithURL:(NSURL *)url {
__weak typeof(self) weakSelf = self;
// Simulate an asynchronous operation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url]; // blocking for example
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf && strongSelf.completion) {
strongSelf.completion(data, nil);
}
});
}
- (void)dealloc {
NSLog(@"%s: deallocated", __func__);
}
@end
The delegate is weak to avoid a cycle if the delegate (often a view controller) holds the client. The completion block is copy (required for blocks that may outlive the creating scope) and captures self weakly via weakSelf, then promotes to strongSelf inside the block.
To verify that the client deallocates when its owner is popped, add a dealloc log as shown and repeatedly push and pop a view controller that creates and starts the client. In Instruments, the Allocations instrument should show the client's live count returning to zero after each pop.
Trade-off: runtime overhead vs. unsafe_unretained
Zeroing __weak references incur a small runtime cost: each access checks whether the referenced object has been deallocated and substitutes nil if needed. unsafe_unretained avoids this check but leaves you with a dangling pointer if the object disappears, leading to crashes. Use unsafe_unretained only when you can guarantee—by a broader invariant—that the referenced object will outlive the holder (for example, an object that owns its delegate).
Actionable checklist
- Scan your code for properties or ivars that reference the owning object; mark them
weakunless you have a proven lifetime guarantee. - For any block stored as a property or passed to an API that may retain it, apply the
__weak … __strongpattern. - Prefer
copyfor block properties and for immutable collections (NSString,NSArray,NSDictionary) to protect against mutable subclasses. - Validate with
dealloclogging, Instruments Leaks and Allocations, and the Clang Static Analyzer.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.