Automatic Reference Counting
In the Cocoa world, the big news from WWDC is the advent of Automatic Reference Counting, or ARC. The only real documentation for the system is an unlinked reference page on the Clang website, but as Clang is open source and the implementation’s in the latest builds now, that counts as public information.
The Cocoa frameworks have long used a reference-count-based system, but as of Mac OS X v10.5, Apple added optional garbage collection. As with most GC systems, you can mark certain references as __weak
(which automatically become nil
when their target is collected), and the actual collection of objects is non-deterministic, meaning you shouldn’t put cleanup code in your finalize
method. (There’s very little that’s safe to do in a finalize
method, actually.) But you can pretty much remove all retain
, release
, and autorelease
messages from your code and stop worrying about memory leaks.
The catch? No garbage collection on iOS, presumably because one extra background thread per app and delayed freeing of memory was considered too high a cost on the resource-limited platforms iOS apps run on. What’s negligible on a modern computer becomes noticeable on a mobile device.
We all thought Apple would keep optimizing the garbage collector, or that the iOS hardware would eventually get powerful enough that the performance hit wouldn’t matter. Instead, though, Apple’s come up with ARC, which you can basically think of as inserting retain
, release
, and autorelease
whenever necessary. All the benefits of garbage collection, without the runtime costs. Magic!
Okay, I’ve never had a problem with reference counting, so maybe the question isn’t “why use ARC?” but “what took so long?”. The closest analogue I know of is Boost’s shared_ptr
, now adopted into C++11. (shared_ptr
also uses reference counting, via RAII.) But of course, the problem with automatic reference counting is cycles: if two objects refer to each other, they’ll never be deallocated.
So, why aren’t cycles a problem with manual reference counting? Well, you just have to distinguish two kinds of references: ones that imply ownership (sub-views, value objects like strings and dates), and ones that don’t (delegates, action targets). This is a mild annoyance, but after a few weeks programming in a framework you usually pick it up pretty quickly.
Now, think about what Apple has done (and NeXT) over the past ten years of Cocoa:
- Simple ownership rules, based on a naming convention.
- Attributes for when you really need to violate that convention.
- Static analysis to enforce the ownership rules.
- A way to specify the ownership semantics of accessors, along with a way to automatically generate those accessors.
- A convention for weak references, which (until now) has been rather loose.
- Autorelease.
Seen in retrospect, it almost looks as if Apple’s been working towards this all along. We’ve now been trained to follow naming conventions and to annotate our code to declare the semantics we want. We hardly ever get it wrong because the rules are generally clear. And if we want RAII-type semantics for Cocoa types, we can do that.
// See http://kickingbear.com/blog/archives/13 to see how it works
{
NSObject *myObject KBScopeReleased = [[NSObject alloc] init];
NSLog( @"%@", myObject );
} // myObject is sent a release message here.
But if the rules are clear, then a program with a good understanding of the source code can put in the retain
s for us.1
That’s what ARC is.
And it can do a better job of knowing when to retain
and release
than you can. (The existence of functions like objc_autoreleaseReturnValue
implies that it may even be able to optimize the autorelease
out of relinquishing ownership at the end of a method call.)
I don’t entirely grok ARC yet; I’m not in the iOS Developer Program, so all I have access to is Clang and its test cases. In particular, converting between managed object pointers and unmanaged C pointers (including CoreFoundation objects) seems to have gotten a lot messier. I hope Apple makes some nice docs available when they release this for real.
But you know what? I tried GC on one of my recent apps, and guess what? It’s nice not to have to worry about retain
and release
! So, I’m in favor of ARC, and I’m ready to have it integrated into my Cocoa development.
Interestingly, this leaves Apple with two managed-memory models on the Mac: ARC and GC. GC is still a little more powerful, since you don’t have to worry about retain cycles at all. (__weak
in GC is only for when you expect an object to go away before you do.) But ARC requires almost zero runtime support, and it’s the only option on iOS besides manual refcounting. My guess is Apple’s going to throw their support behind ARC and let GC languish.
On the flip side, the discussion about Apple’s switch from GCC to LLVM/Clang finally has a piece of solid evidence. AFAIK, GCC does not have an ARC implementation, and I doubt it will get one. Apple now has a compiler chain that’s not only not GPL, but that they essentially control. (A number of the leaders in the LLVM community, including the project founder Chris Lattner, work at Apple. The next biggest contributer is Google.) Which is what everyone’s been suspecting they’ve been after all along.
P.S. Autorelease pools look funny now.
@autoreleasepool {
NSString *fileName = [input lastPathComponent];
NSString *baseName = [fileName stringByDeletingPathExtension];
NSString *extension = [fileName pathExtension];
return [NSString stringWithFormat:@"Base: %@\nExtension: %@",
baseName, extension];
}
-
If you’re a person who doesn’t like the compiler mucking with things behind your back, think of it this way: almost always, you want to retain your objects while you’re using them and release them when you’re done. So instead of marking ownership with
retain
/release
, you should only have to annotate the special case of a__weak
reference. ↩︎