Subclassing NSMutableDictionary

Although I haven’t posted much, if anything, on Cocoa programming I thought I’d post a bit more over the next few months. I’ve been coding in Cocoa for quite a few years now and it is, by far, the most productive application coding environment I’ve ever worked in. Unfortunately, the scope of Cocoa is huge and, if it wasn’t for other Cocoa programmers posting hints, notes and tips, I’d still be scratching my head about topics like drag-and-drop weirdness with NSTokenField bindings (more on this in a later post).

So this is to try to repay the favor to other coders who’ve struggled with the same things I have. And where better to start than one of the uglier problems that many programmers run in to; subclassing class clusters. The Cocoa designers, in their infinite wisdom, decided to make a few of the core Cocoa classes based on a class cluster and not concrete. This makes them awkward to try to subclass as you have to work out all of the primitive methods and provide support for them in the subclass.

Most folks end up giving up in disgust and wrapping things like NSMutableDictionary. However, once in a while, you really, really, really need to subclass it. In my case, I wanted a basic class that I could use in RapidAlbum that would call a delegate when an object in the dictionary was added or changed (so that I could flag a page export). However, I really wanted a dictionary interface as I wanted to be able to leverage all the existing NSDictionary methods (like dictionaryFromDictionary:) as the whole point is to avoid writing more code.

The trick here is to catch setObject:forKey: and removeObject:forKey: and add a delegate call. Note that if you could easily register a wildcard key-value observer for a container class that tracked all key/object changes in the class, then this would be unnecessary. That’s an expletive-laden rant for another day.

There’s a few pointers out there as to how to subclass NSMutableDictionary but nothing that really covers all of it. So, without further ado, here’s the approach I took.

I started with the composite object technique described by Apple in the documents below

Class Clusters, Technical Note TN2059

The first note from Apple talks about composite objects and illustrates the technique using an NSMutableArray. The second one actually lists example code in the guise of illustrating lock technique. The basic approach is that you create a subclass of your class cluster (NSMutableDictionary) as a wrapper around the actual NSMutableDictionary you want to use. Then you start plugging in all the primitive methods required to wrap the access to the internal sub-dictionary. This is the rather ugly (but workable) technique that Apple recommends.

The catch? Apple developer documentation is weak at listing what’s actually required to make a fully functioning subclass.

To subclass NSMutableDictionary using composite objects, here’s what you need to provide

    - (id)init;
    - (id)initWithCapacity:(unsigned int)numItems;
    - (unsigned)count;
    - (NSEnumerator *)keyEnumerator;
    - (id)objectForKey:(id)aKey;
    - (void)removeObjectForKey:(id)aKey;
    - (void)setObject:(id)anObject forKey:(id)aKey;

Note -initWithCapacity: is in there. This is mentioned as an “optimization” in the Technical Note but it seems to be mandatory for keyed decoding. Two other methods required in order to prevent ripping your hair out when using keyed encoding/decoding are

    + (Class)classForKeyedUnarchiver
    - (Class)classForKeyedArchiver

If you don’t return the class of your subclass from these methods, the keyed decoder will happily decode your objects from the archive but render them as NSMutableDictionary instead of your subclass. Whilst this magic normally happens automatically for subclasses of concrete classes, it looks like you have to brew your own manual potions for subclasses of class clusters.

And that’s it. Apart from bindings. Ah. Bindings. One of the additional sources of joy is making your subclass actually work with bindings. The problem here is that the automagic NSDictionary binding goodness appears to break down when you use composite objects. This appears to be because external objects bind to the NSMutableDictionary wrapper (which is “empty”) and not to the internal sub-dictionary. The only workaround I’ve found for this that seems to work is to generate your own key messages around the access to the internal sub-dictionary. In your setObject:forKey: method, wrap the access to the sub-dictionary like this

    [self willChangeValueForKey:aKey];
    [subDictionary setObject:anObject forKey:aKey];
    [self didChangeValueForKey:aKey];

This appears to keep all of the external bindings to the subclass happy.

So, there you have it. An actual NSMutableDictionary subclass. Just like watching sausages being made.

Leave a Reply