Objective-C Method Family Conventions
Understand the four core Objective-C method families — init, new, copy/mutableCopy, and everything else — and how both ARC and its historical predecessor, manual reference counting, infer ownership purely from a method's name prefix rather than its return type.
Objective-C's automatic memory management, whether the modern compiler-driven ARC or the manual retain/release discipline that came before it, depends on a surprisingly simple mechanical rule: a method's NAME tells you who owns the object it returns. Not its return type, not a comment, not documentation — its name, and specifically the prefix of its name. This rule is called method family convention, and it is one of the load-bearing ideas underneath everything covered in the earlier Manual Reference Counting, ARC, Object Creation: alloc and init, and Initialization Patterns modules. This lesson isolates that one idea and examines it in full: what the four method families are, exactly how the compiler recognizes them by inspecting a selector's spelling, and what happens — both under modern ARC and under the historical manual reference counting it replaced — when a method's name doesn't honestly reflect the ownership of what it returns.
What a Method Family Is
A method family is a group of methods that Objective-C's compiler and runtime treat as making the same ownership promise about the object they return, based entirely on how the method's name begins. There are four families relevant to ownership: methods beginning with alloc, methods beginning with new, methods beginning with copy, and methods beginning with mutableCopy. Any method whose name starts with one of these four prefixes is understood to return an object the CALLER now owns — meaning the caller is responsible for eventually releasing it (under manual reference counting) or, under ARC, the object simply behaves as a +1, caller-owned reference that ARC will release once nothing holds a strong reference to it anymore.
Every other method — anything that doesn't begin with alloc, new, copy, or mutableCopy — is assumed to return an object the caller does NOT own outright. Such a method is understood to return what's traditionally called an autoreleased, or +0, reference: an object the caller can use immediately and safely, but has no long-term ownership claim over, unless the caller explicitly takes a strong reference to it (which, under ARC, happens automatically the moment you assign the result to a strong-qualified property or local variable — the object simply survives as long as something is holding it, exactly like any other object).
The word 'family' captures something important: this isn't a single fixed list of method names Apple maintains somewhere. It's a naming PATTERN. Any method anywhere — in Foundation, in UIKit, in your own app's code, in a third-party library — that happens to start with one of these four prefixes is automatically enrolled in that family's ownership convention, whether or not the person who wrote it intended that. This is precisely why the convention is dangerous to violate by accident, as the mistake example later in this lesson demonstrates.
The Four Families in Detail
The alloc/init/new family covers object creation and initialization. +alloc, by itself, allocates memory for a new instance and returns it as a caller-owned +1 reference with no initialization performed beyond zeroing its memory. -init and its many variants (-initWithName:, -initWithFrame:, and so on, covered at length in the Initializer Design lesson) take that freshly-allocated, caller-owned instance and finish setting it up, returning that same caller-owned reference back — ownership is simply carried through the alloc/init chain, never transferred away. +new is a convenience shorthand that internally performs [[self alloc] init] in one message send, and it inherits the same caller-owned +1 convention as a result, since it is, underneath, just alloc followed by init.
The copy/mutableCopy family covers object duplication. -copy (and the more explicit -copyWithZone: that backs it) returns a new, independent, caller-owned instance that is a duplicate of the object it was called on — typically an immutable duplicate, if the original class has both mutable and immutable variants, like NSString versus NSMutableString. -mutableCopy follows the identical ownership convention but explicitly returns a mutable duplicate instead — calling -mutableCopy on an NSArray returns a caller-owned NSMutableArray, for instance. Both methods exist because sometimes you need your own private, independent copy of an object rather than merely another reference to the same shared instance, and both hand that new copy to the caller as a full +1 owned reference, exactly like alloc/init does.
Everything else — which includes an enormous number of everyday Objective-C methods, most importantly factory and convenience methods — falls outside all four ownership families and returns an autoreleased, +0 reference instead. A class method like + (instancetype)stringWithFormat:(NSString *)format, ... on NSString, or + (instancetype)arrayWithObjects:... on NSArray, or any custom factory method you write yourself like + (instancetype)buttonWithTitle:(NSString *)title, returns an object the caller can use safely and immediately, but does not own outright unless it's explicitly retained (manually, historically) or assigned into a strong-qualified reference (automatically, under ARC).
How the Compiler Infers Ownership from the Name, Not the Return Type
The single most important — and most commonly misunderstood — detail of method family conventions is that the compiler determines a method's family by inspecting its SELECTOR, the literal text of its name, and specifically the prefix of the first selector piece. It does not look at the method's return type at all when making this determination. A method returning instancetype, NSString *, id, or any other type is treated identically from an ownership-inference standpoint; what matters is only whether the method's name begins with alloc, new, copy, or mutableCopy (following specific casing and word-boundary rules — the prefix must be followed by a capital letter or the end of the selector piece, so initialize does NOT count as beginning with init in the family sense, but initWithName: does).
This name-based inference is what makes ARC's static analysis possible without needing any runtime type information at compile time. The compiler, scanning your source code, sees a call to a method whose selector starts with alloc, new, copy, or mutableCopy, and inserts the appropriate retain/release bookkeeping (under the hood, even in ARC, though you never write it yourself) on the assumption that this method follows the family convention correctly. It never actually inspects what the method's implementation does internally — it trusts the name. This trust-the-name design is efficient and consistent, but it also means the entire system depends on every method author honestly naming their methods according to what they actually return.
Historical Manual Reference Counting Interpretation
Before ARC existed, Objective-C developers applied this exact same naming convention by hand, with real consequences for correctness if they got it wrong. Under manual reference counting (MRC), covered in depth in the Manual Reference Counting module, a caller who received an object from an alloc/init/new or copy/mutableCopy family method was expected to call -release (or -autorelease) on it themselves once done with it, because the convention promised them a +1, caller-owned reference — failing to eventually release it was a memory leak. A caller who received an object from any other method (a factory or convenience method, an accessor, and so on) was expected to NOT release it, because the convention promised an autoreleased +0 reference already scheduled for cleanup by whatever autorelease pool was active — calling -release on such an object anyway, believing incorrectly that you owned it, caused a premature deallocation and a dangling-pointer crash the next time anything tried to use it.
This meant that, under MRC, every Objective-C developer had to mentally track method family conventions constantly and manually, for every single method call in their code, because getting it wrong in either direction — over-releasing or leaking — produced a real bug with no compiler safety net to catch the mistake. ARC's single biggest contribution was automating exactly this bookkeeping: the compiler now inserts the correct retain/release calls itself, based on the very same naming-convention inference MRC developers used to perform by hand, which is precisely why method family conventions remain just as relevant under ARC today as they were under MRC — ARC didn't replace the convention, it only automated compliance with a convention that was there from the beginning.
The Four Ownership-Transferring Prefixes
The prefix must be followed by a capitalized word boundary or the end of the piece — initialize is NOT in the init family, but initWithName: is. Return type (instancetype, id, a concrete class) plays no role in family inference — only the selector's spelling does. ARC inserts retain/release calls automatically based on this same naming inference; you never write them yourself.
// alloc/init/new family — returns a caller-owned (+1) reference
Person *p1 = [Person alloc]; // alloc
Person *p2 = [[Person alloc] init]; // alloc + init
Person *p3 = [Person new]; // new == [[Person alloc] init]
// copy/mutableCopy family — returns a caller-owned (+1) reference
NSString *s2 = [s1 copy];
NSMutableArray *m2 = [array mutableCopy];
// everything else — returns an autoreleased (+0) reference
NSString *s3 = [NSString stringWithFormat:@"%d", 42];
Person *p4 = [Person personNamed:@"Ada"]; // factory method, NOT init/alloc/new/copyCode Examples
Both alloc and initWithName: are members of the alloc/init/new family. The compiler recognizes the selector prefixes and treats the returned object as caller-owned throughout the chain.
-copy is in the copy/mutableCopy family, so it returns a fully independent, caller-owned duplicate — not merely another reference to the same underlying string storage.
Even though the method internally uses alloc/init to build the object, the METHOD'S OWN NAME — personNamed: — does not begin with alloc, new, copy, or mutableCopy, so it is treated as returning an autoreleased +0 reference to any caller, regardless of what happens inside its implementation.
This is exactly the manual bookkeeping ARC eliminated: a developer had to remember, for every single method call, whether its name placed it in an ownership family, and release accordingly — get it wrong and you leak memory or crash on a dangling pointer.
Common mistake: Calling -release on the result of stringWithFormat: (an autoreleased, +0 factory method) is a classic MRC bug — it releases an object the caller never owned, causing a crash the next time anything touches it.
Person *person = [[Person alloc] initWithName:@"Grace"];
// person is a caller-owned (+1) reference under ARC.
// Under MRC, the caller would be responsible for [person release]
// once done with it.NSMutableString *original = [NSMutableString stringWithString:@"Draft"];
NSString *snapshot = [original copy];
// snapshot is an independent, caller-owned (+1) copy.
// Mutating `original` afterward has no effect on `snapshot`.
[original appendString:@" v2"];
NSLog(@"%@", snapshot); // still prints "Draft"+ (instancetype)personNamed:(NSString *)name {
Person *p = [[self alloc] initWithName:name];
return p; // ARC autoreleases this on the way out, since
// -personNamed: is not in any ownership family.
}
Person *guest = [Person personNamed:@"Ada"];
// guest is a +0 reference the caller does not own outright —
// under ARC it survives only because `guest` is a strong local.// Historical manual reference counting (pre-ARC)
Person *person = [[Person alloc] initWithName:@"Grace"];
[person doSomething];
[person release]; // required: alloc/init family promised a +1 reference
NSString *label = [NSString stringWithFormat:@"Hello, %@", name];
[label release]; // WRONG under MRC: stringWithFormat: is autoreleased (+0),
// not caller-owned; this causes a premature dealloc.Method Family Ownership Inference
The compiler walks this chain purely from the selector's text — a method's return TYPE never enters the inference at any step.
Method Name
↓
Method Family
↓
Ownership Convention
↓
ARC BehaviorWhere This Shows Up
Static analyzers, including Clang's own built-in analyzer that ARC relies on, flag exactly this class of naming mistake automatically — a method named like a family member (starting with new, alloc, copy, or mutableCopy) whose implementation clearly does not allocate or copy a fresh object triggers a compiler warning, precisely because the mismatch between name and actual behavior is a well-known, common source of memory bugs. This is also why Apple's own official naming guidelines are so insistent that ordinary factory and accessor methods avoid accidentally starting with one of the four family prefixes — a method named newDocumentTemplate when it actually just returns a shared singleton template is a landmine for exactly the reason shown in the mistake example above.
+ (instancetype)newPersonFromCache { return _cachedPerson; // returns a +0, shared/borrowed reference }
+ (instancetype)cachedPerson { return _cachedPerson; // name no longer implies ownership transfer }
Naming a factory method newPersonFromCache puts it in the new family purely by virtue of its name's prefix, even though the method returns a shared, cached instance rather than a fresh caller-owned object. Under ARC, the compiler trusts the name and treats the returned reference as caller-owned (+1), inserting a retain on the way out that the actual implementation never intended — while under historical MRC, a caller who dutifully released this object as convention demanded would over-release a shared instance still in use elsewhere, corrupting the cache. The fix is simply to avoid the new/alloc/copy/mutableCopy prefixes on any method that does not actually hand the caller a fresh, independently-owned object.
Which four prefixes place a method in an ownership-transferring family? A. get, set, is, has B. alloc, new, copy, mutableCopy C. init, dealloc, retain, release D. create, make, build, construct
B. alloc, new, copy, mutableCopy — The four family-defining prefixes are alloc, new, copy, and mutableCopy. Methods beginning with any of these are assumed to return a caller-owned (+1) reference.
What does the compiler inspect to decide which method family a method belongs to? A. The method's return type (instancetype, id, or a concrete class) B. The method's selector — specifically, the prefix of its first name piece C. Whether the method is declared in the @interface or only the @implementation D. The number of parameters the method takes
B. The method's selector — specifically, the prefix of its first name piece — Method family inference is based entirely on the selector's spelling — the prefix of the method name — not on the method's return type or any other declaration detail.
A factory method + (instancetype)personNamed:(NSString *)name that internally uses alloc/init returns what kind of reference to its caller? A. A caller-owned (+1) reference, because it uses alloc/init internally B. An autoreleased (+0) reference, because its own name doesn't start with alloc, new, copy, or mutableCopy C. It depends on the method's return type D. It is undefined behavior and must be documented separately
B. An autoreleased (+0) reference, because its own name doesn't start with alloc, new, copy, or mutableCopy — Ownership inference looks only at the outer method's own name, not what it does internally. Since personNamed: does not begin with a family prefix, it is treated as returning an autoreleased +0 reference regardless of its internal implementation.
Under historical manual reference counting (MRC), what happened if a developer called -release on the result of an autoreleased factory method like +stringWithFormat:? A. Nothing — release calls on autoreleased objects were always safe B. It was a memory leak, since the object was never actually freed C. It over-released the object, risking a dangling pointer and a crash on later use D. MRC did not support factory methods at all
C. It over-released the object, risking a dangling pointer and a crash on later use — Autoreleased +0 objects were not owned by the caller, so manually releasing one on top of the autorelease pool's own eventual release over-released the object, potentially deallocating it prematurely and leaving a dangling pointer for the next use.
Why is naming a method +newPersonFromCache, when it actually returns a shared cached instance, considered a mistake? A. new methods are not allowed to be class methods B. The new prefix falsely signals a fresh, caller-owned object, causing the compiler/ARC to treat a shared instance as if it were independently owned C. Cached objects can never be returned from Objective-C methods D. It would cause a compile-time syntax error
B. The new prefix falsely signals a fresh, caller-owned object, causing the compiler/ARC to treat a shared instance as if it were independently owned — The name alone, not the implementation, determines the ownership convention the compiler applies. A new-prefixed method that actually returns a shared instance creates a mismatch between the promised ownership and the real behavior, leading to incorrect retain/release bookkeeping and potential bugs.
A colleague writes a method + (NSArray *)copyOfDefaultSettings that actually just returns a reference to a shared, mutable, module-level NSArray instance without duplicating it at all. Explain what goes wrong under ARC, and rename the method so its name honestly reflects its behavior.
Because the method's name begins with copy, ARC treats it as a member of the copy/mutableCopy family and assumes it returns a fresh, caller-owned duplicate — inserting retain/release bookkeeping on that assumption. Since the implementation actually returns a shared instance with no duplication performed, callers end up holding what they believe is an independent copy but is in fact the same shared array everything else references, and mutations by one caller silently affect all the others. Renaming it to something like + (NSArray *)defaultSettings (dropping the copy prefix) removes the false ownership promise and correctly signals that callers receive a shared, autoreleased reference.
Think about which family the copy prefix places this method in, and what ARC assumes about any method in that family.