Skip to content
Memory Management and ARC
step 1/4

Reading — step 1 of 4

Learn

~2 min readMemory, Runtime, Modern OC

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 reference
  • weak — no retain; nilled on release; safer than assign
  • copy — 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_getAssociatedObject for 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…