Managing Objective-C objects within an iOS application is always challenging due to the fixed amount of physical memory and the limited battery life of handheld devices. To address these challenges, Apple has introduced a new approach called "automatic reference counting" (ARC). In this article, I compare ARC against explicit retain/release and against garbage collection, as well as demonstrate automatic reference counting in an iOS project, and review some key guidelines on using ARC.

Readers should have a working knowledge of Objective-C and of the Xcode IDE.

Managing by Messaging

Initially, I manage ObjC objects through explicit messaging. I create the object using alloc and init messages (Figure 1). I send a retain message to hold onto the object, and a release message to dispose of it. ObjC objects made with alloc/init start with an internal retain count of one. A retain message increases that count by one, while a release message decreases it by one. When the retain count reaches zero, the object self-destructs, freeing memory it has reserved.



Figure 1.

We can also use a factory method to create the ObjC object. This marks the object for autorelease , adding its pointer to the autorelease pool (Figure 2). We can do the same to an alloc/init object by sending the latter an autorelease message.



Figure 2.

The pool checks its collection of object pointers during each event cycle. When it finds an object that is out-of-scope and has a retain count of one, it disposes of that object with a release message. To deter disposal, we can send one or more retain messages to the autoreleased object. Then to allow disposal, we counter the retain s with an equal number of release messages.

Explicit messaging is still the best way to manage ObjC objects within an iOS application. It takes almost no overhead, it is easy to debug, and it can be fine-tuned for performance.

On the other hand, explicit messaging is prone to errors. An uneven number of retain and release messages can cause a memory leak or an EXC_BAD_ACCESS error. An explicit release to an autoreleased object can also lead to an EXC_BAD_ACCESS . And a collection object (like an array or a set) may not self-destruct when some of its entries have retain counts greater than one.

Managing by Garbage Collection

MacOS X 10.5 (Leopard) gave us another way to manage ObjC objects  garbage collection. Here, each Cocoa application gets its own collection service, which runs as a secondary thread (Figure 3).



Figure 3.

The service identifies all the root objects created right after launch time, then tracks every object created afterwards. It checks each one for scope and for strong references to a root. If the object has these attributes, the service lets it persists (marked in blue). Otherwise, it disposes of the object with a finalize message (marked in red).

The collection service is a conservative one. It can be interrupted, even suspended, when high performance is required. It is a generational service. It assumes the most recent objects to have the shortest lifespans.

Access to the collection service is through the class NSGarbageCollector . With this class, I can disable the service or change its behavior. I can even assign new root objects or reset the service itself.

Garbage collection removes the need for explicit retain and release messages. It can reduce dangling pointers and guard against null pointers. On the other hand, it requires all custom ObjC objects to be updated. Clean-up code must go into the finalize method, not the dealloc method. The ObjC object also must send a finalize message to its parent.

Next, the collection service needs to know when an object reference is weak. Otherwise, it assumes all references to be strong. This can lead to circular references and to memory leaks. The service also ignores objects created with malloc() : Those should be disposed of manually or created using the Cocoa function NSAllocateCollectable() .

Finally, the service still incurs a performance hit despite being conservative. This is one reason why garbage collection is absent on iOS.

Enter ARC

ARC is an innovative approach that has many of the benefits of garbage collection, but without the performance costs.

Internally, ARC is not a runtime service. It is, in fact, a deterministic two-part phase provided by the new Clang front-end. Figure 4 shows the two parts of that phase. In the front-end phase, Clang examines each pre-processed file for objects and properties. It then inserts the right retain , release , and autorelease statements based on some fixed rules.



Figure 4.

For example, if the object is allocated and local to a method, it gets a release statement near the end of that method. If it is a class property, its release statement goes into the class' dealloc method. If the object is a return value or is part of a collection object, it gets an autorelease statement. And if the object is weakly referenced, it is left alone.

The front-end also inserts retain statements for objects not locally owned. It updates any accessors declared with the directive @property . It adds calls to the parent's dealloc routine, and it reports any explicit management calls and any ambiguous ownership.

In the optimize phase, Clang subjects the modified sources to load balancing. It counts the retain and release calls made for each object, then pares them to the optimal minimum. This avoids excessive retain s and release s, which can impact on overall performance.

To demonstrate, look at the sample code in Listing One.

Listing One

@class Bar; @interface Foo { @private NSString *myStr; } @property(readonly) NSString *myStr; - (Bar *)foo2Bar:(NSString *)aStr; - (Bar *)makeBar; //... @end @implementation Foo; @dynamic myStr; – (Bar *)foo2Bar:(NSString *)aStr { Bar *tBar; if (![self.myStr isEqualToString:aStr]) { myStr = aStr; } return ([self makeBar]); } - (Bar *)makeBar { Bar *tBar //... //... conversion code goes here //... return (tBar); } //... @end

Here, I show an ObjC class bereft of any retain/release messages. It has one private property myStr , which is an instance of NSString (line 5). It declares a read-only getter, also named myStr (line 7). It defines a modifier foo2Bar and an internal function makeBar (lines 18-36). The class also imports the header for class Bar using the @class directive (line 1).

Listing Two shows the same sample code post-ARC.

Listing Two

@class Bar; @interface Foo { @private NSString *myStr; } @property (readonly) NSString *myStr; - (Bar *)foo2Bar:(NSString *)aStr; - (Bar *)makeBar; //... @end @implementation Foo; @dynamic myStr; – (Bar *)foo2Bar:(NSString *)aStr { Bar *tBar; if (![self.myStr isEqualToString:aStr]) { [aStr retain]; [myStr release]; myStr = aStr; } return ([self makeBar]); } - (Bar *)makeBar { Bar *tBar //... //... conversion code goes here //... [tBar autorelease]; return (tBar); } //... - (void)dealloc { [myStr release]; [super dealloc]; } @end

The class interface remains unchanged. But its foo2Bar modifier got two new statements. One statement sends a release message to property myStr (line 24). Another sends a retain statement to argument aStr (line 25). The makeBar function sends an autorelease message to local tBar before returning the latter as its result. Finally, ARC overrides the class' dealloc method. In that method, it releases the property myStr (line 44) and invokes the parent's dealloc method (line 45). If a dealloc method is already present, ARC will update its code appropriately.

Since ARC decides on its own how an ObjC object should be managed, it cuts the time spent on developing the class code. It prevents any dangling and null pointers. It can even be disabled on a per-file basis. This last feature lets the programmer reuse legacy sources of proven reliability.

But the Clang compiler is built into LLVM 3.0, which is available only on Xcode 4.2 and newer. Optimized runtime support for ARC is present only on MacOS X 10.7 (Lion) and iOS 5.0. It is possible to use ARC in iOS 4.3 binaries through glue code. It is also possible to use ARC in OS X 10.6 binaries, provided the latter does not employ any weak pointers

Next, ARC works exclusively with ObjC sources. It has no effect on PyObjC and AppleScriptObjC sources. It does, however, affect the underlying ObjC code that bridges each PyObjC and ASOC class to Cocoa. Also of note, some third-party frameworks may cause errors when compiled with ARC enabled. Make sure to contact the framework developer for updated versions.