ARC was introduced in XCode 4.2. Immediately, the technology became shrouded in an aura of mystery. Experts proclaimed that there is no mystery. The technology is completely deterministic. It may be deterministic, but, exactly how the system behaves under different situations is not very well documented. In this article, I will try to explain the basics through examples.
Consider the code:
@public
NSString *city;
}
@end
@implementation Address
– (Address*) init: (NSString*) c {
city = c;
return self;
}
– (void) dealloc {
NSLog(@"Destroying address: %@", city);
}
@end
@interface Customer : NSObject {
NSString *name;
Address *addr;
}
@end
@implementation Customer
– (Customer*) init: (NSString*) n withAddress: (Address*) a {
//Note 1: Automatically retain on assignment
name = n;
addr = a;
return self;
}
– (void) dealloc {
NSLog(@"Destroying: %@", name);
//Note 2: All member variables are released automatically
}
@end
Customer* objectReturnTest() {
NSString * n = initWithString: @"Billy Bob"];
Address * a = init: @"New York City"];
Customer *c = init: n withAddress: a];
//Note 3: ARC will put the returned object in autorelease pool.
return c;
}
A couple of basic things to note here. As “Note 1” says, when an object is assigned to a variable, a call to retain is made automatically. This increments the reference count. As “Note 2” says, when an object is destroyed, all member variable objects are released for you. You no longer have to do that from the dealloc method.
Finally, when a method returns a newly created object, ARC will put the returned object in an autorelease pool. This is stated in “Note 3”.
Now, let’s use the code.
{
NSString * n = initWithString: @"Johnny Walker"];
Address * a = init: @"Miami"];
Customer *c = init: n withAddress: a];
NSLog(@"Before force release");
c = nil; //Force a release
NSLog(@"After force release");
@autoreleasepool {
Customer *c2 = objectReturnTest();
}
NSLog(@"After autorelease pool block.");
return 0;
}
The log output from this code will be:
Before force release
Destroying: Johnny Walker
After force release
Destroying: Billy Bob
Destroying address: New York City
After autorelease pool block.
Destroying address: Miami
A couple of things to note here. See how force release works. We set a variable to nil. ARC immediately releases the reference count. This causes the Customer object “Johnny Walker” to get destroyed. But, the member Address object “Miami” doesn’t get destroyed. That is because the Address variable a still has a reference to that object. This Address object gets destroyed at the very end of the main method.
The object return test works as expected. Customer “Billy Bob” is put in auto release pool. At the end of the @autoreleasepool block, the pool is drained and the object is released.
Finally, we will now test for another common scenario. Objects may be created within a method that are not returned. Exactly when are they released and destroyed. The answer is, at the end of the method. Here is an example:
Address * a1 = init: @"Los Angeles"];
Address * a2 = init: @"Seattle"];
}
We can call the method as follows:
temporaryObjectTest();
NSLog(@"After method call");
The log output will be:
Before method call
Destroying address: Seattle
Destroying address: Los Angeles
After method call
These temporary objects are not put in autorelease pool. They are simply released at the end of the method. Only a new object returned from a method is autoreleased.