Autorelease the Hounds

I’ve been thinking about weak references in ARC.

Let’s say I’ve got a new application model layer, for describing urban transportation infrastructure:

Diagram of a tree of objects

The topmost object is an “Urban Transportation” object, which owns a list of city objects (in this case, “New York” and “San Francisco”). Under each city object are the kinds of transportation in that city, such as cars, buses, and airports (planes). Each object owns the things underneath it and is responsible for disposing of them (releasing them) when they’re removed. Good so far.

The odd man out in the diagram above is the flight object, “Flight 2077”. It’s owned directly by the top-level object, for lack of anyone else to provide direct ownership. But the two airports underneath the “New York” and “San Francisco” objects each have what I’ll call a “cross-tree” reference to it, since it’s a flight between those two cities.

What should those cross-tree references look like?

In retain/release, you have two options, and they’re both bad. You could use unretained pointers. Those have the advantage of not creating retain cycles, but they’re dangerous because they’ll continue to refer to the flight object even after it’s been released.

Or you could use retained pointers and add what I’ll call an “invalidate” method to the flight. “invalidate” methods rely on the fact that, even when the retain cycle hasn’t been broken yet, you know from your application’s logic when your object should go away. At that time, you call “invalidate” on it, and then it breaks the cycles for you. In this case, the flight object would have to contain its own pointers back to the referring airports, and be able to tell the airports to remove their pointers/retains to itself. The more classes in your model that can have cross-tree pointers, the more “invalidate” methods you need, to the point where you’d probably just want to make it a mandatory protocol. Lots of extra logic, easy to get wrong.

In garbage collection, you’ve got a better option. You can use weak pointers. That way, once all the strong references are removed (in this case, the reference from the “Urban Transportation” object), the weak references in the airport objects would be set to nil, and the flight object would be deallocated. Easy peasy. You could still use an “invalidate” method here, but it’s not necessary. (Though it’s still a good idea for releasing other sorts of resources under GC without employing a finalize method, which Apple discourages.)

In ARC, you can also use weak pointers, in exactly the same way you use them in GC. But is that an undesirable amount of overhead? The whole point of ARC is that it’s fast and immediate. But the more weak pointers you use, the more you’re forcing the system to keep track of all your pointers and objects, in a way very reminiscent of GC, and not at all like the rest of ARC, which just drops in just the right number of invisible retains and releases at compile time and calls it a day.

My gut feeling here, though, is that the overhead won’t be that much unless you’ve got an overwhelmingly large number of objects. Won’t know till I try it!

7/15/2012 Note: The title is actually taken from one of the rejected episode titles of the latest Edge Cases episode, which we recorded before I posted this, even though the episode itself was only released afterwards. Wolf’s thoughts definitely informed my final edit of this post. That title actually came from Vincent Gable’s tweet, however.