Snow Leopard: The Underhyped APIs
In my article Grand Central Dispatch: Exciting or Overhyped?, I take a look at one of the most hyped parts of Snow Leopard: the Grand Central Dispatch APIs for creating concurrent applications. In this article, I'm going to look in the opposite direction: at some of the new APIs that will make life much easier for developers, but which haven't received anything like as much hype.
Apple has a history of adding massive new features for developers with each release. Key-value Observing, accompanied by Cocoa Bindings, eliminated the need to write controllers for a lot of applications. CoreData did the same thing for Model objects. The last two releases of OS X haven't had anything quite so major, but have had a lot of incremental improvements that make Cocoa a lot of fun to develop with.
Caches and Discardable Data
One of my favorite additions in Snow Leopard is the NSDiscardableContent protocol. This defines a simple transactional model for interacting with objects. It defines four new methods which any object can implement to support this model of interaction.
When you use an object that implements this model, you should bracket your accesses with calls to the methods to begin and end editing. These lock the data represented by the object in memory, but at any other time the object is allowed to discard it in response to a -discardContents call.
Only one new class in Cocoa implements this protocol[md]an NSMutableData subclass[md]but it's very easy to add to your own classes.
By itself, this protocol is not so interesting. It really becomes powerful when combined with something like NSCache. This is a class for which I've been asking for several years.
My PhD was in out-of-core prefetching algorithms, and one of the things that I found particularly frustrating was that most modern operating systems (L4 Hurd being the notable exception) did not provide any means of hinting to an application that data should be discarded, rather than swapped out. This is important, because often the cost of re-creating the data if it's needed again is less than the cost of handling a load of page faults.
The documentation on NSCache is quite vague. It doesn't give any clues as to the policy it uses for eviction, and even hints that it may not ever evict things. You can use NSCache like an NSMutableDictionary, but with one major difference. NSCache may evict entries that implement the NSDiscardableContent protocol.
When you insert an object into a cache, you associate a cost with it. You can also set a maximum cost for the cache and a maximum number of items. When either of these is exceeded, the cache will try evicting items.
The cache doesn't have to respect these values, and it may evict objects at random times. This means that it may, for example, try to evict all of its contents when the system is low on memory, as an alternative to swapping. It's not clear whether this happens in the current version, but if it doesn't then it almost certainly will in future.
Integrating discardable data and normal objects in your application can be difficult. Every object that sends messages to an object representing discardable data must remember to call the transactional methods. You can eliminate this requirement quite easily using the -autoContentAccessingProxy method. This returns a proxy that forwards all messages on to the real object. When the proxy is created, it calls -beginContentAccess on the object, and when it is destroyed it calls -endContentAccess. This ensures that the objects contents will not be discarded as long as the proxy exists.
This kind of proxy has been possible since the NeXT days, but is traditionally quite slow. Last time I benchmarked message forwarding on OS X, it took 600 times as long as a direct message send, making it something that you want to avoid whenever possible.