Ownership Qualifiers in Depth: __strong, __weak, __unsafe_unretained, __autoreleasing
Explain the four ARC ownership qualifiers — __strong, __weak, __unsafe_unretained, and __autoreleasing — precisely when each applies to local variables, instance variables, and properties, and how they correspond directly to the strong/weak/unsafe_unretained/copy property attributes already covered.
The previous ARC lesson introduced strong, weak, unsafe_unretained, and autoreleasing primarily through the lens of @property attributes and everyday patterns like delegates and NSError ** out-parameters. This lesson goes one level deeper: these four names are not only property attributes — they are the underlying ownership qualifiers the compiler attaches to every single Objective-C object pointer variable in an ARC program, written with two leading underscores (__strong, __weak, __unsafe_unretained, __autoreleasing) when spelled out explicitly on a variable declaration, and applied automatically by default in the overwhelming majority of code you actually write. Understanding them as a variable-level concept, not just a property-level one, is what lets you correctly reason about ordinary local variables, instance variables declared without a property, C-style arrays of object pointers, and blocks capturing variables from their enclosing scope — all places where a property attribute keyword doesn't apply, but an ownership qualifier still governs exactly the same question: does this pointer keep its object alive?
Ownership Qualifiers Are a Type Feature, Not Just a Property Feature
Under ARC, every variable whose type is an Objective-C object pointer — NSString *, id, a pointer to any class — carries one of the four ownership qualifiers as part of its full type, whether or not you write that qualifier explicitly. The full, technically complete type of a variable declared as NSString *name; inside a method is actually NSString * __strong name; — the compiler infers __strong because that is the default qualifier whenever none is specified, for exactly the same reason strong is the default property attribute discussed in the ARC lesson. You can write the qualifier explicitly — NSString * __weak name; — and doing so is required, not optional, in a small number of situations this lesson covers (declaring a weak local variable, for instance, or working with certain C-level constructs like arrays of object pointers where property syntax simply does not exist).
Recognizing that these four keywords are type qualifiers, applying to any object pointer variable anywhere, is what makes the rest of ARC's behavior fall into place as one consistent system rather than a set of special cases to memorize separately for properties, ivars, and locals. A property attribute like @property (nonatomic, weak) id delegate; is really just a convenient, higher-level syntax that tells the compiler to synthesize a backing instance variable typed id __weak _delegate; — the underlying mechanism is identical, whether the qualifier arrived via a property declaration or was written directly on a variable.
__strong: The Default Everywhere
__strong is the default ownership qualifier for local variables, instance variables, and properties alike, whenever nothing else is specified — this consistency is deliberate, precisely because a program in which every reference defaulted to non-owning would be constantly at risk of objects being deallocated out from under code that still needed them. A __strong local variable keeps its object alive for the variable's entire scope: assign a freshly created object to a __strong local, and that object is guaranteed to survive at least until the end of the block the variable is declared in, even if every other reference to it disappears in the meantime, because the local variable's own strong hold is enough on its own.
This is worth stating explicitly because it explains a pattern you will see constantly in real code: temporarily assigning something to a local __strong variable specifically to guarantee it survives for the duration of a block of code, even when you don't otherwise need the variable name for anything else. A common example is capturing self as a strong local inside a block, when the block itself only captures self weakly (to avoid a retain cycle) but needs a guarantee that self won't be deallocated partway through the block's own execution: __weak typeof(self) weakSelf = self; outside the block, then __strong typeof(self) strongSelf = weakSelf; as literally the first line inside the block body, upgrading the weak capture to a strong local for just long enough to safely use it.
__weak: Requires Zeroing-Weak-Reference Runtime Support
A __weak-qualified variable, exactly as with the weak property attribute, does not keep its object alive and is automatically zeroed to nil by the runtime the instant that object is deallocated — the diagram and delegate example from the previous lesson apply identically here, at the variable level. What the property-focused discussion in the previous lesson didn't dwell on is that this zeroing mechanism has a hard runtime requirement: it depends on Objective-C's modern runtime maintaining a side table of weak reference locations that it can find and zero out during an object's deallocation, and this capability was not available on every Objective-C runtime Apple ever shipped — it requires the modern (64-bit-capable, non-'legacy') Objective-C runtime, which is why weak was simply unavailable as an option at all in the very earliest days of ARC on certain older 32-bit iOS targets, before Apple's runtime updates caught up. On every actively supported Apple platform today, this is no longer a practical concern, but it is exactly why unsafe_unretained exists as a fallback option at all, rather than weak being the only non-owning qualifier ever offered.
A __weak local variable is written exactly as you'd expect — __weak NSObject *observer = someObject; — and behaves identically to a weak property: usable and safe to message right up until the moment its referent is deallocated, at which point it silently becomes nil rather than dangling. One easy mistake with weak locals specifically is assigning a freshly-alloc'd object directly to a weak local with nothing else strongly retaining it: __weak NSObject *obj = [[NSObject alloc] init]; creates an object with no strong owner anywhere, so it is deallocated essentially immediately (often before the very next line runs), and obj is left nil right away — the compiler will typically even warn about this specific pattern, because it is almost never what anyone intends.
__unsafe_unretained: The Non-Zeroing Fallback
__unsafe_unretained behaves, at the variable level, exactly as it did as a property attribute in the previous lesson: it does not retain its object, and it is not tracked or zeroed by the runtime, so once the object it refers to is deallocated, the variable is left holding a dangling pointer that is unsafe to dereference or message. As a variable-level qualifier, its primary legitimate uses are the same two situations already introduced: targeting an older or legacy runtime that lacks zeroing-weak-reference support, and interoperating with specific APIs — historically, certain block-related and collection APIs — that are declared to expect unsafe_unretained semantics rather than weak.
Because __unsafe_unretained requires no runtime bookkeeping at all, it is marginally cheaper than __weak in raw performance terms — no side-table entry needs to be registered or cleared — but this performance difference is virtually never significant enough to justify the safety tradeoff in ordinary application code, and reaching for it as a default 'lightweight weak' is exactly the mistake called out in the previous lesson's mistakes list. The one place you are likely to still deliberately choose __unsafe_unretained today is when working with C-style structures or arrays of object pointers where the compiler cannot generate the required weak-reference bookkeeping at all — ARC's weak reference machinery has restrictions on exactly which storage locations it can manage, and a raw C array or certain union/struct layouts fall outside what it supports, forcing a choice between __unsafe_unretained and manually managing the memory outside ARC entirely for that particular storage.
__autoreleasing: Implicit on Out-Parameters
__autoreleasing, as a variable-level qualifier, applies almost exclusively to the pointee of an id * (or specifically-typed, like NSError **) parameter used as an out-parameter, exactly as the ARC lesson's NSError example demonstrated. Writing __autoreleasing explicitly yourself is rare enough that most working Objective-C programmers go long stretches without ever typing it — it exists primarily so the compiler has a name for the qualifier it infers automatically on your behalf whenever you take the address of a strong local variable (&error, where error is declared NSError *error = nil;) and pass it to a parameter typed as a pointer-to-object-pointer.
The reason this qualifier needs to exist at all, rather than out-parameters simply using __strong, comes back to exactly the ownership question this whole lesson is organized around: the method receiving the out-parameter is handing back an object it does not intend to retain long-term ownership of itself, so the value it writes through that pointer needs to be guaranteed to survive at least long enough for the caller to read it, without either side permanently owning it beyond that. Autoreleasing the value, registering it with the current pool, accomplishes exactly that: it survives comfortably past the method call returning, for at least as long as the current autorelease pool remains undrained, which in ordinary application code is always long enough for the immediate calling code to use the value.
How Ownership Qualifiers Apply to Local Variables
For an ordinary local variable inside a method body, the qualifier governs exactly how long, and how certainly, the variable itself keeps its object alive within its own scope. A __strong local guarantees its object survives for the variable's entire lexical scope, independent of what any other code does with other references to the same object in the meantime — this is precisely why a __strong local is the right tool whenever you need a guaranteed-alive handle to something for the duration of a block of work, even if you already have a weak reference to the same object available elsewhere. A __weak local gives you no such guarantee — the object might be deallocated between one line and the next if nothing else strongly retains it — which is exactly why the strongSelf-inside-a-block idiom from earlier in this lesson exists: it converts a weak capture into a temporary strong local specifically to get that scope-long guarantee for just the block body that needs it.
How Ownership Qualifiers Apply to Instance Variables
An instance variable declared directly in an @implementation or @interface block (outside of @property syntax) is subject to exactly the same default-strong rule as a local variable: @implementation Reporter { id __weak delegate; } explicitly marks that raw ivar weak, while @implementation Reporter { id delegate; } with no qualifier defaults it to strong. Before @property syntax existed at all (in very old Objective-C code) and in the relatively rare cases today where a class deliberately declares a raw ivar instead of a property (an ivar that is purely internal bookkeeping, never exposed via any accessor), the ownership qualifier written directly on that ivar declaration is doing exactly the same job the corresponding property attribute would do if one existed.
This is the direct throughline to the Properties lesson's attribute list: strong, weak, and unsafe_unretained as property attributes are not a separate vocabulary from __strong, __weak, and __unsafe_unretained as ownership qualifiers — they are the exact same underlying concept, expressed through two different syntaxes for two different contexts (a declared property versus a raw variable). When you write @property (nonatomic, weak) id<ReporterDelegate> delegate;, you are telling the compiler to synthesize a backing ivar whose type includes the __weak qualifier and a setter that assigns into it accordingly; the property attribute is sugar over exactly the ivar-qualifier mechanism this section describes directly. Copy, notably, is a property-only concept with no direct ownership-qualifier equivalent — it governs whether the setter stores a copy of the incoming value rather than the value itself, a decision about what gets stored, layered on top of (and still ultimately using) __strong to hold that stored copy alive.
Choosing the Right Qualifier: A Practical Decision Process
In practice, choosing the right ownership qualifier for any given object pointer — local, ivar, or property — comes down to a short, ordered set of questions. First: does this reference need to keep the object alive on its own, independent of anything else? If yes, it's __strong (or the equivalent strong property attribute), which covers the overwhelming majority of references in ordinary code — most data your objects hold onto, most local variables you're actively working with. Second, if the reference should not keep the object alive (a parent-to-child back-reference, a delegate, anything that would otherwise create a cycle with a strong reference already pointing the other way): does the target platform's runtime support zeroing weak references? On every actively maintained Apple platform today, the answer is always yes, so the choice becomes __weak. Third, only if you have a specific, concrete reason weak is unavailable or unwanted for that particular reference (a legacy target, or a storage location ARC's weak machinery cannot manage, such as certain raw C aggregates) do you drop down to __unsafe_unretained, accepting the dangling-pointer risk deliberately and narrowly rather than by default. __autoreleasing, finally, is not really a choice you make at all in everyday code — it is the qualifier the compiler infers for you automatically at the one specific place it's needed, the pointee of an object out-parameter.
Code Examples
The first two lines are equivalent -- __strong is the inferred default whenever no qualifier is written. The third line creates a weak local that will not, by itself, keep the object alive or prevent it from deallocating once 'implicit' goes out of scope; the fourth creates an unsafe_unretained local that shares that same lack of ownership but additionally will not be zeroed to nil, unlike weakRef, once the object it refers to is gone.
NSObject * __weak weakRef = implicit; // non-owning, but safely auto-nils
This is exactly the mechanism a @property (nonatomic, weak) id delegate; declaration would generate for you automatically -- a backing ivar qualified __weak, and a setter that simply assigns into it without retaining. Writing it by hand here makes visible exactly what the property attribute is sugar over. In ordinary modern code you would almost always prefer the @property syntax; this raw-ivar form is shown specifically to demonstrate the underlying mechanism.
The block captures self only weakly (via weakSelf), avoiding a retain cycle between self and the block it stores in self.completionHandler. Inside the block body, weakSelf is immediately upgraded to a __strong local, strongSelf, guaranteeing self cannot be deallocated partway through the block's own execution once the block has actually started running -- exactly the scope-long guarantee __strong locals provide, applied deliberately for just the duration of this block.
__strong typeof(self) strongSelf = weakSelf;
Common mistake: Using strongSelf (or self directly) throughout the block without the weakSelf/strongSelf dance at all, which recreates the exact retain cycle between self and the block that this pattern exists to avoid.
The weak property attribute and the __weak ownership qualifier are the same underlying mechanism seen through two syntaxes. Declaring the property with weak instructs the compiler to synthesize a backing ivar whose type carries the __weak qualifier, plus a setter that simply assigns rather than retaining -- precisely the raw-ivar pattern from the previous example, generated automatically. This is the direct link back to the Properties lesson: strong/weak/unsafe_unretained as property attributes map one-to-one onto __strong/__weak/__unsafe_unretained as ownership qualifiers on the synthesized backing ivar; copy is the one property-only concept layered on top, governing what gets stored rather than how the storage is owned.
NSObject *implicit = [[NSObject alloc] init]; // implicitly __strong
NSObject * __strong explicitStrong = [[NSObject alloc] init];
NSObject * __weak weakRef = implicit; // does not keep 'implicit' alive on its own
NSObject * __unsafe_unretained unsafeRef = implicit; // does not keep it alive, and won't auto-nil@implementation Reporter {
id __weak _delegate; // raw ivar, explicitly weak -- no @property involved
}
- (void)setDelegate:(id)delegate {
_delegate = delegate; // assigning into a __weak ivar; no retain happens
}
@end__weak typeof(self) weakSelf = self;
self.completionHandler = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return; // self was already deallocated; nothing safe to do
}
[strongSelf updateUIAfterCompletion];
};@interface Reporter : NSObject
@property (nonatomic, weak) id<ReporterDelegate> delegate;
@end
// What the compiler effectively generates for the backing storage:
// id<ReporterDelegate> __weak _delegate;
// - (id<ReporterDelegate>)delegate { return _delegate; }
// - (void)setDelegate:(id<ReporterDelegate>)delegate { _delegate = delegate; }Where explicit qualifiers show up in real, everyday code
Working Objective-C engineers rarely write __strong or __autoreleasing explicitly, because the compiler's defaults and its inference on out-parameters already do the right thing almost everywhere. The two qualifiers you do see written out by hand constantly are __weak, in the weakSelf/strongSelf block-capture idiom shown in this lesson (a fixture of essentially every codebase using completion-handler blocks, GCD callbacks, or delegate-style APIs backed by blocks), and, far more rarely, __unsafe_unretained, almost always accompanied by a code comment explaining the specific legacy or API-compatibility reason it was chosen over weak, precisely because a reviewer seeing __unsafe_unretained without an explanation should reasonably ask why weak wasn't used instead.
Assigning a freshly alloc'd object directly to a __weak local variable: __weak NSObject *obj = [[NSObject alloc] init];
Assign the fresh object to a __strong (ordinary) local first, and only create a weak reference to it if and where a non-owning reference is actually needed elsewhere.
With nothing else strongly retaining the freshly created object, it has no owner at all the instant this line finishes, so ARC deallocates it almost immediately, leaving obj nil right away -- almost never the intended behavior, and the compiler typically warns about exactly this pattern.
Capturing self strongly inside a block stored as a property on self (e.g. self.completionHandler = ^{ [self doWork]; };) without a weak/strong dance.
Capture self weakly outside the block, then upgrade to a strong local (strongSelf) as the first line inside the block body before using it.
self strongly owns the block (via the property), and if the block also strongly captures self, the two now strongly own each other, forming a retain cycle that keeps both alive forever even after they should be deallocated.
Declaring a raw ivar meant to avoid a retain cycle as plain id delegate; (no qualifier) and assuming it is automatically non-owning.
Explicitly qualify it id __weak delegate; (or, more idiomatically, use a weak @property).
__strong is the default for any object pointer with no explicit qualifier, ivars included -- omitting a qualifier does not make a reference non-owning, it makes it strong, which is the opposite of what a cycle-avoiding back-reference needs.
Using __unsafe_unretained as a general-purpose 'lighter-weight weak' in ordinary application code with no specific compatibility reason.
Default to __weak (or the weak property attribute) for any non-owning reference on modern Apple platforms; reserve __unsafe_unretained for legacy-runtime targets or storage locations ARC's weak machinery genuinely cannot manage.
__unsafe_unretained is not tracked or zeroed by the runtime, so once its object is deallocated it becomes a dangling pointer rather than safely becoming nil -- the exact dangling-pointer risk that __weak exists specifically to eliminate.
What is the default ownership qualifier for an Objective-C object pointer variable when none is written explicitly? A. __unsafe_unretained B. __autoreleasing C. __strong D. __weak
C. __strong — __strong is the default everywhere -- local variables, raw ivars, and properties alike -- specifically because defaulting to a non-owning reference everywhere would put objects at constant risk of being deallocated while still needed.
Why does __weak require the modern Objective-C runtime specifically, rather than working on any runtime? A. It doesn't -- __weak has always worked identically on every runtime Apple has ever shipped B. The zeroing behavior depends on the runtime maintaining a side table of weak reference locations it can find and clear during an object's deallocation, a capability the legacy runtime lacked C. __weak requires a garbage collector to be enabled D. __weak only works on properties, never on plain variables
B. The zeroing behavior depends on the runtime maintaining a side table of weak reference locations it can find and clear during an object's deallocation, a capability the legacy runtime lacked — Zeroing weak references depend on runtime-level bookkeeping (a side table tracking every weak reference to a given object) that only the modern Objective-C runtime provides; this is exactly why __unsafe_unretained existed as a fallback for older/legacy runtime targets.
In the weakSelf/strongSelf block pattern, what is the purpose of creating strongSelf as the first line inside the block? A. To permanently convert the weak capture into a strong one for the rest of the object's lifetime B. To guarantee self cannot be deallocated partway through the block's own execution, for just the duration of that block C. To silence an unrelated compiler warning about unused variables D. To avoid having to capture self in the block at all
B. To guarantee self cannot be deallocated partway through the block's own execution, for just the duration of that block — weakSelf avoids a retain cycle between self and the block, but gives no guarantee self survives for the block's whole execution. Upgrading it to a __strong local at the top of the block body gives exactly that scope-long guarantee, only for as long as the block itself runs.
How does a weak @property attribute relate to the __weak ownership qualifier? A. They are unrelated -- property attributes and ownership qualifiers are two entirely separate systems B. The weak property attribute is sugar over synthesizing a backing ivar qualified __weak, plus a non-retaining setter -- the same underlying mechanism C. weak properties use __unsafe_unretained internally for performance reasons D. weak as a property attribute only affects documentation and has no effect on generated code
B. The weak property attribute is sugar over synthesizing a backing ivar qualified __weak, plus a non-retaining setter -- the same underlying mechanism — Declaring a property weak instructs the compiler to synthesize a backing ivar whose type carries the __weak qualifier and a setter that assigns without retaining -- exactly the mechanism you get by hand-declaring a raw __weak ivar and setter yourself.
Which ownership qualifier is the compiler-inferred default for the pointee of an NSError ** out-parameter, and why? A. __strong, because the calling method should permanently own the error B. __unsafe_unretained, because errors don't need to be tracked C. __autoreleasing, because the value needs to survive at least until the caller can read it, without either side needing to own it long-term D. __weak, because errors should become nil immediately after being logged
C. __autoreleasing, because the value needs to survive at least until the caller can read it, without either side needing to own it long-term — __autoreleasing guarantees the value written through the out-parameter survives at least until the next autorelease pool drain -- long enough for the calling code to inspect it -- without the called method retaining it long-term or the caller needing to manage it explicitly.
A raw ivar is declared as 'NSObject *cachedItem;' with no explicit qualifier, inside a class where this reference is intended to avoid creating a retain cycle. What is wrong? A. Nothing -- raw ivars are always non-owning by default B. It defaults to __strong, which keeps the referenced object alive and does not achieve the intended non-owning, cycle-avoiding behavior C. It will fail to compile because ivars require an explicit qualifier D. It behaves identically to __autoreleasing
B. It defaults to __strong, which keeps the referenced object alive and does not achieve the intended non-owning, cycle-avoiding behavior — Just like local variables and properties, a raw ivar with no explicit ownership qualifier defaults to __strong -- it must be explicitly written as __weak (or __unsafe_unretained, with good reason) to get non-owning behavior.
A class has a raw ivar, without any @property, declared as: id observer; intended to hold a reference to an object that should NOT be kept alive by this class (to avoid a cycle), and should safely become nil if that object is deallocated elsewhere. Identify what is wrong with the declaration as written, and correct it.
As written, 'id observer;' has no explicit ownership qualifier, so it defaults to __strong -- meaning this class DOES keep the referenced object alive, which is the opposite of the stated intent, and it will never auto-nil since __strong references simply keep their object alive rather than clearing themselves. Corrected: 'id __weak observer;' -- this does not keep the object alive, and will be automatically zeroed to nil by the runtime the instant the object it refers to is deallocated.
Recall the default ownership qualifier for any object pointer with no explicit qualifier, and which of the four qualifiers both avoids ownership and auto-nils.