I want to understand ARC, and I'm reading this:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#consumed-parameters
It says that a consumed parameter is retained before the call, and released at the end of the function (inside the function body). It then says that init...
methods are effectively marked with ns_consumes_self
. I don't see the point.
Foo *foo = [[Foo alloc] initWithWhatever: x];
So alloc
returns an object with a retain count of 1, right? Now it is retained again before going into init
, and then released at the end of init
, so we're back at 1. Why is it designed like this? When I think about what the typical init
looks like, I get more confused.
self = [super init];
if (self) { ... }
return self;
Probably because initializers are free to return a pointer to a different object than the one that
self
points to initially. An-init
method can say: I don't like this object, I think I'll substitute a different one and that's perfectly OK. Before ARC, such an initializer would explicitly releaseself
(even though it never retained it) and then assign some other pointer to it. Presumably, thens_consumes_self
directive will take care of releasing the object that was passed into the initializer even ifself
is changed to point to some other object inside the method.It's a good bet that the behavior is there to cover cases that don't look like a typical
-init
method. Modifyingself
isn't exactly typical, it's just permissible.Why shouldn't it be that way? Calls to
-init
are meant to return a given object with a +1 retain count, and doing a "temporary retain" is the safest way of guaranteeing that self remains alive throughout the entirety of a given init method. Consider what happens if we peel back the layer of Objective-C abstraction and turn-init
into the function pointer it's IMP resolves to:If given a
self
with a +0 retain count (as is common in most methods that retain their arguments i.e. setters), then you would have to guarantee that somewhere up the inheritance chain someone was nice enough to retain self away from whatever happened to allocate it (ending inself
having a rather ambiguous retain count of +1). But, if you receive a self with a +1 retain count, and you do a retain-release it yourself,you can be certain that it remains alive through your-init
method, and that you and you alone have ownership ofself
. And if the given self was not "alive", then you're guaranteed to return nil rather than an object in the middle of deallocation. Thus, the above becomes (in pseudo-C):I like to call this pattern "old-school atomic access", and it's used in Apple frameworks designed before
@synchronized {}
atomic getters were invented. When you use a getter backed with a public iVar. You'll often see methods that follow that pattern written like:But all of this doesn't touch the ownership rules of the exception that is
-init
and family.-init
methods return objects +1, but who exactly owns them? Well, the allocator* still has a hand in the reference to the variable, andself = [super init]
doesn't actually retain anything (and it also has to obey the whole "returns +1" rule). Well, again I have to turn to pseudo-code, but this time it'll be in Objective-C:OK, so now you've got a +1 object floating around claimed by the allocator, so how do you "claim" it? Assignment, of course! The point of implicitly
__strong
locals and__strong
properties is to reclaim the object from the allocator entity.*Allocator, in the context of this answer, refers to the functions that eventually call
malloc()
to allocate space for the object.