Reading — step 1 of 4
Learn
Objective-C historically required manual reference counting — every alloc paired with a release. Modern OC (since 2011) uses ARC (Automatic Reference Counting) where the compiler inserts those calls.
Reference counting basics
Every object has a retain count. retain increments; release decrements. When count hits 0, dealloc runs and memory is freed.
%% Manual (pre-ARC):
NSString *s = [[NSString alloc] initWithCString:"hello" encoding:NSUTF8StringEncoding];
%% s has retain count 1
[s retain];
%% retain count 2
[s release];
%% retain count 1
[s release];
%% retain count 0 — dealloc runs
ARC — modern Objective-C
With ARC enabled (default in modern Xcode):
NSString *s = [[NSString alloc] initWithCString:"hello" encoding:NSUTF8StringEncoding];
%% Compiler-inserted: retain on assignment
%% When s goes out of scope: release
No manual retain/release — just write code as if you have a GC.
ARC ownership qualifiers
For variables:
__strong(default) — owns; bumps retain count__weak— non-owning; auto-nils when target deallocated__unsafe_unretained— non-owning, doesn't auto-nil (for pre-iOS 5 compat)__autoreleasing— for out-parameters
For properties:
strong(default) — owns, like Java referenceweak— no retain; nilled on release; safer thanassigncopy— for value types like NSString (defends against mutable subclass passing)assign— for primitives only (not for objects in modern code)
@property (strong) NSString *name; %% retains
@property (weak) id<DownloadDelegate> delegate; %% doesn't retain — avoids cycle
@property (copy) NSString *title; %% copies on set (safer for strings)
Retain cycles
Most common ARC bug: A holds B, B holds A, neither ever deallocates.
@interface Parent
@property (strong) Child *child;
@end
@interface Child
@property (strong) Parent *parent; %% CYCLE — both retain each other
@end
Fix: one side uses weak:
@property (weak) Parent *parent; %% no retain — cycle broken
Block retain cycles
Very common: a block captures self, which holds the block:
self.completion = ^{
[self doSomething]; %% block retains self
};
%% self retains the completion block, which retains self → cycle
Fix with weak self:
__weak typeof(self) weakSelf = self;
self.completion = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) [strongSelf doSomething];
};
Ugly but necessary. Swift's closure capture lists handle this more cleanly with [weak self].
When to NOT use ARC
- Bridging with C++ that has its own object model
- Fine-grained memory control for embedded
- Existing pre-ARC codebase under maintenance
99% of new ObjC code uses ARC.
Inspecting reference counts
Don't. Seriously — retainCount exists but is unreliable in ARC. The compiler optimizes away calls; the count you see is meaningless. Instead:
- Use Instruments (Xcode profiler) to find leaks
- Use
objc_getAssociatedObjectfor advanced cases - Trust ARC; verify with leak detection
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…