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
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
releasemessage decreases it by one. When the retain count reaches zero, the object self-destructs, freeing memory it has reserved.
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.
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
retains with an equal number of
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
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).
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
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
Finally, the service still incurs a performance hit despite being conservative. This is one reason why garbage collection is absent on iOS.
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
autorelease statements based on some fixed rules.
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
deallocroutine, 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
release calls made for each object, then pares them to the optimal minimum. This avoids excessive
releases, which can impact on overall performance.
To demonstrate, look at the sample code in Listing One.
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.
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
autoreleasemessage 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.
Preparing for ARC
There are two ways to support ARC in an iOS project. One way is to use an ARC-enabled template to create the project. Another is to have Xcode adapt an existing project for ARC.
Suppose you want to start a new iOS project. Launch Xcode and choose New Project from the File menu. On the new assistant dialog (Figure 5), select a project template (in this example, Single View Application), and click Next to view the project options. Enter the project name, company ID, and class prefix in the fields provided. Then click to set the check-box Use Automatic Reference Counting (Figure 6). Click Next to view the project location. Set the location and the source repository (optional), then click Create to create the project itself.
To verify if ARC is enabled, select the project icon from the Groups and Files pane in the project window (Figure 7). Click on Build Settings from the Target Settings toolbar, then click All underneath that toolbar. Scroll down and locate the settings group Apple LLVM computer 3.0 — Language. Look for the entry labeled Objective-C Automatic Reference Counting — it should have a value of Yes.
What if you want to adapt an existing iOS project to ARC? Open the project into Xcode and click on the Edit menu. From the Refactor submenu (Figure 8), choose Convert to Objective-C ARC… This brings up another assistant dialog summarizing the steps to be taken.
Click Next to see a list of build targets (Figure 9). Select a target and click Precheck to start refactoring. If refactoring fails, Xcode will alert the user, and it will list the refactor errors on the project window.
If the refactor succeeds, Xcode goes into compare mode (Figure 10). The assistant dialog splits into three panes. The left pane displays the affected files, with the first file selected. The middle pane show the original source text, the right pane shows the modified source. Review the proposed changes and click Save to commit them or Cancel to dispose of them. Make sure to have a code repository in place, however. Otherwise, you will be unable to revert the project files to their pre-ARC state.
What if you want to exclude some project files from ARC? In this case, click to clear the checkbox next to each file while Xcode is in compare mode (see Figure 10). If the file was added to a refactored project, select the project icon from the Groups and Files pane. Click Build Phases on the Target Settings pane, and scroll down to the group Compile Sources (Figure 11). Click on the group header and select which files to exclude. Then click into the Compile Flags column and enter
-fno-objc-arc into the modal dialog. Click Done to set the flag, which should appear next to each file.
Guidelines for ARC
When writing ObjC code from scratch, you should ensure the code is compatible with ARC. That code must give ARC the clues it needs to do its tasks. Otherwise, it may insert statements in the wrong spots, or worse, it may report false errors in the code.
Here are some guidelines for writing ARC-compatible code. These are compiled from the official Apple docs and from blog articles listed at the end of this article.
- Let ARC decide how and when to retain and release objects.
As stated earlier, the ARC front-end catches any explicit
releasemessages. It will report them as errors, and you will have to remove them manually to let the project compile. So never send a
releasemessage to an ObjC object. Never check the object’s
retainstatus with a
retainCountmessage because that message is no longer reliable. Also, do not use the directive
@selectorto invoke the object’s
@propertyaccessors, declare them without the attributes
retain. Let ARC decide on which attribute to give each accessor.
- Let ARC manage the autorelease pool and its objects.
Again, the ARC front-end catches any explicit autorelease messages. Make sure to exclude these messages in your ObjC code.
Also, do not use
NSAutoreleasePoolto create the pool. Instead, use the
@autoreleasedirective to mark the location and scope of the pool. This tells the front-end to insert statements that create and dispose of pools optimized for ARC.
Consider the sample code in Listing Three.
NSArray *tArgs = [[NSProcessInfo processInfo]
unsigned tCnt, tLmt = [tArgs count];
(tCnt = 0; tCnt < tLmt; tCnt++)
tNom = [tArgs objectAtIndex:tCnt];
tDat = [[[NSString alloc]
// Process the file, creating and
// autoreleasing more objects
// Do more tasks, creating and autoreleasing
// more objects
// Do whatever cleanup is needed
main()entry function uses two autorelease pools. The first pool appears near the start of the function (line 7) and stays active until the function ends (line 28). Any objects marked for autorelease by the function go to this pool.
The second pool appears at the start of a
for-loop cycle (line 11-23). As each cycle ends, it disposes of the pool and creates a new one in its place. Objects marked for autorelease during each loop go to this pool, not to the first one.
- Declare explicitly any weak and unretained references.
ARC assumes all object references to be strong. There are exceptions, of course, the best example being the window view (Figure 12). The view keeps strong references to each subview and widget it contains. On the other hand, each subview and widget only keeps a weak reference to its parent view.
However, it is possible for two ObjC objects to refer to each other strongly. When this happens, you get a circular reference. Consider the sample class in Listing Four.
@property(readwrite, assign) FooLink *nextLink;
@property(readonly) FooLink *prevLink;
- (NSData *)getData;
- (FooLink *)searchForLink:(NSInteger)anID;
FooLinkcreates a double-linked list, with each link holding an
fooPrevhold pointers to other instances of
FooLink. Since both pointers form a strong reference, ARC will not dispose of the instance.
To prevent this, declare one of the properties as weak. In Listing Five, I placed the directive __weak before the property fooPrev (line 4). Thus, when fooNext points to a nil and the FooLink instance is out of scope, ARC can safely send a release message to the instance.
__weak FooLink *fooPrev;
@property(readwrite, assign) FooLink *nextLink;
@property(readonly) FooLink *prevLink;
- (NSData *)getData;
- (FooLink *)searchForLink:(NSInteger)anID;
__weakdirective declares a reference that can be both weak and a nil. The alternative directive
__unsafe_unretaineddeclares one that is weak, but not a nil. Use this directive if you plan to zero out the references yourself. But be sure to do, or you will end up with a memory leak.
There are other compiler directives, of course, but the aforementioned two are the ones you will use often.
- Avoid recasting ObjC objects into C-pointers.
Pointers to an ObjC object are of the generic type
id. To use them as input to a C-routine, you could recast the pointer as shown by Listing Six (lines 3-9). Here, I used the factory method
stringWithString:to create an
NSStringinstance. Then I recast it into an integer pointer, and pass that pointer to the routine
// Recasting an ObjC object pointer
tStr = [NSString stringWithString:@
tPtr = (
// Using a CFObject
tStr = CFSTR(
However, recasting prevents ARC from managing the object correctly. ARC will not know when to retain the object and when to dispose of it. Moreover, the recast pointer could end up pointing to an invalid object. So, instead of recasting, use Core Foundation APIs to create a C-compatible object (lines 14-17).
- Avoid using ObjC objects as fields in a C
We can use ObjC objects as fields inside a C
struct. The sample
structin Listing Seven has two such fields (lines 4-5): one an instance of
NSString, the other of
NSURL. If the ObjC object is a custom one, we could render its properties visible with the
// A C struct with ObjC fields
// A C struct with CoreFoundation fields
// The C struct rewritten as an ObjC class.
//...property accessors goes here
But here too, ARC will be unable to manage an ObjC object inside a
struct. It could not identify references made to that object and provide the right
autoreleasestatements for that object. Again, use Core Foundation APIs to create objects for a
struct(lines 13-16). Or rewrite the
structas a simple ObjC class (lines 20-28).
- Manage all C and CF objects yourself.
Like garbage collection, ARC ignores all objects created using
stdliband Core Foundation APIs. It will not insert calls to
free(), which are needed to create and dispose of a
structor union. Nor will it insert calls to
CFRelease()to retain or dispose of a CF object.
Make sure to supply these calls yourself. Take care to balance each call to
malloc()with a call to
free()and each call to
CFRelease(). Also make sure to avoid calls to
CFRelease()twice on the same object.
Automatic reference counting is an innovative way of managing ObjC objects on MacOS X 10.7 and iOS 5. It does away with explicit
autorelease messages, and it cuts down potential memory leaks and null pointers. It offers optimized autorelease pools and it avoids the same runtime overhead as garbage collection. It even behaves the same on both platforms. However, there are aspects of ARC too complex to be covered in this space. To learn more about ARC, consult the references listed below.
Apple Developer. “Advanced Memory Management Programming Guide.” MacOS X Developer Library.https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf[PDF].
Apple Developer. “NSGarbageCollector Class Reference.” Mac OS X Developer Library.https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSGarbageCollector_class/NSGarbageCollector_class.pdf[PDF].
Apple Developer. “Transitioning to ARC Release Notes.” iOS Developer Library.http://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/RN-TransitioningToARC.pdf [PDF].
Apple Developer. “What’s New in Xcode.” MacOS X Developer Library.http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/WhatsNewXcode/WhatsNewXcode.pdf[PDF].
LLVM Project. “Automatic Reference Counting.”http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Mike Ash. “Automatic Reference Counting.” http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html.
Mugunth Kumar. “WWDC 2011, Session 323: Introducing Automatic Reference Counting.”http://blog.mugunthkumar.com/articles/migrating-your-code-to-objective-c-arc.
Paul Long. “Understanding Automatic Reference Counting in Objective C.”http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained.