retain it’s objects, and
copy it’s keys.
Here are some effects this has had on code I’ve worked on.
Sometimes you get the same object you put in, sometimes not.
Immutable objects are optimized to return themselves as a
copy. (But with some exceptions!). So the following code:
NSDictionary *d = [NSDictionary dictionaryWithObject:@"object" forKey:originalKey]; for(id aKey in d) if(aKey == originalKey) NSLog(@"Found the original key!");
Might print “Found the original key!”, and might not, depending on how
[originalKey copy]is implemented. For this reason, never use pointer-equality when comparing keys.
- Mutable objects make bad keys. If
xis a mutable
[x copy] is animmutable copy of
x, at that point in time. Any changes to
xare not reflected in the copy. For example,
[dict setObject:x forKey:key]; //...code that changes key, but not dict assert([[dict objectForKey:key] isEqual:x]); //fails!
copyis an immutable object, it will blow up if you try to mutate it.
NSMutableString *key = //something... [dict setObject:x forKey:key]; for(NSMutableString *aKey in dict) [aKey appendString:@"2"]; //Error, aKey isn't mutable, even though key is!
View objects make bad keys. Views have state related to the screen: their
frame, position in the view hierarchy, animation layers, etc. When you
copya view object, the copy won’t (always) be
isEqual:to the original, because it’s not on the screen in exactly the same way.
Your classes must support
NSCopyingto be used as a key in an
NSDictionary, you can’t just implement
-isEqual:in your custom classes.
Of course, this isn’t a complete list of every way key-copying can trip you up. But if you understand what
copy means in Cocoa, and remember how
NSDictionary works, you’ll be able to avoid or quickly solve any issues.
How to Document Such Behavior Better Than Apple Did
Given what we know about
NSDictionary, what’s wrong with the following snippit from
@interface NSMutableDictionary : NSDictionary - (void)setObject:(id)anObject forKey:(id)aKey; @end
aKey needs to implement
NSCopying, so it should be of type
(id<NSCopying>) instead of type
(id). That way, the header is self-documenting, and, if like most smart programmers, you’re using autocomplete to type out Cocoa’s long method names, the auto-completed template will be self-documenting too.