I’m trying to merge two NSDictionaries:
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *combinedAttributes = [areaAttributes mutableCopy];
[combinedAttributes addEntriesFromDictionary:gpsAttributes];
But get the following error:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: 'The key 'latitude' is not defined for this NSKnownKeysDictionary'
latitude is a key in gpsAttributes
The problem here is that you’re not dealing with ordinary dictionaries, but with
NSKnownKeysDictionary, which is an undocumented type that Apple uses in various parts of Cocoa.If you dump the ObjC class info from the SDK—or, if that sounds too scary, read someone else’s version—you can see that this is actually a subclass of
NSMutableDictionary, not justNSDictionary, so it’s already mutable, so yourmutableCopyisn’t necessary.More importantly, if you read up on how the
NSCopyingandNSMutableCopyingprotocols are defined, there’s nothing that requiresmutableCopyto return you something that’s mutably-compatible with its base class; only that it be mutable, and immutably-compatible with its base class. Which is annoying.The answer here is to generate a new mutable dictionary, and copy the keys from both
NSKnownKeysDictionaryobjects into that:(Obviously, if you’re worried about performance, you either want to tweak that
20, or get the count of keys from each dictionary and pass in the total. But usually, it doesn’t matter.)You may be wondering why Apple did this. But if you think about it, it’s a handy thing. You’ve got an object that can be used like a C
structwithin the guts of some library, but externally looks just like anNSDictionary. (A similar purpose to, e.g.,namedtuplein Python.) The methods that return them are all designed to returnNSDictionary, notNSMutableDictionary, so nobody’s going to know they have a restricted set of keys. Except, of course, if they callmutableCopy.You may also be wondering how you can know about this in advance. Well, you really can’t. Either you log the types of the objects, you write an exception handler and log the exception, or you let it crash and find the exception in the crash dump. Or, alternatively, you can be defensive and assume that any dictionary can give you a useless
mutableCopyand shallow-copy it into a dictionary of a type you control.