Edit: Problem defined below actually occurred with this code:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
XYZPerson *myPerson = [XYZPerson person];
myPerson = nil;
NSLog(@"The end.");
}
}
The method 'person' is a factory method.
I have the following code:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
XYZPerson *myPerson = [[XYZPerson alloc] init];
myPerson = nil;
NSLog(@"The end.");
}
}
XYZPerson overrides dealloc method so that it prints out something with NSLog. I expect the code above to output something like:
Dealloc!
The end.
But it is not as I expected:
The end.
Dealloc!
Am I doing something wrong or did I misunderstand the concept of ARC?
ARC guarantees that objects will be automatically reference counted at compile time. It goes further and places the requirement that the code be algorithmically fully coherent (which manifests as errors when trying to convert between, say,
void*
andid
via casting -- under ARC, you have to qualify the memory management policy across such casts).ARC is not a garbage collector; there is no scanning, no threading, and no stop-the-world behavior. This means more predictable behavior at the cost of things like automatic cycle detection.
While ARC guarantees that an object's lifespan will be automatically managed, ARC does not guarantee that lifespan beyond "the object will live for at least as long, maybe longer, than it is used in the code".
In fact, you might see lifespan changes depending on both the optimization level of the code and whether or not the factory method you invoked is compiled in an ARC vs. manual-retain-release [MRR] source file. And the lifespan may change across releases of the compiler and/or runtime.
For example, ARC code calling into a factory method can sometimes short-circuit the
autorelease
entirely.Sounds scary, but it isn't because of the algorithmic coherence requirement. Since there cannot be ambiguous behavior (as there can in plain old MRR), that the lifespan might change over releases should not impact your code.
Of course, this means that you should not have order dependencies between
dealloc
methods. This should not be an onerous requirement as having order dependencies betweendealloc
methods under MRR was always a nasty thing.This is because ARC still respects the Cocoa memory-management naming conventions. You can add attribute to Your factory method
person
like this:+ (instancetype)person __attribute__((objc_method_family(new)));
so ARC assumes that the object it returns comes with an incremented retain count that will need to be balanced with a corresponding release. Then immediately after setting variable to nil the dealloc will occur.