Home  > Resources  > Blog

Understanding Automatic Reference Counting (ARC)

 
February 9, 2012 by Bibhas Bhattacharya
Category: Mobile Computing

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:

@interface Address : NSObject {
@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.

int main (int argc, const char * argv[])
{
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:

void temporaryObjectTest() {
Address * a1 = init: @"Los Angeles"];
Address * a2 = init: @"Seattle"];
}

We can call the method as follows:

NSLog(@"Before method call");
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.

Follow Us

Blog Categories