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 just NSDictionary
, so it's already mutable, so your mutableCopy
isn't necessary.
More importantly, if you read up on how the NSCopying
and NSMutableCopying
protocols are defined, there's nothing that requires mutableCopy
to 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 NSKnownKeysDictionary
objects into that:
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *combinedAttributes = [NSMutableDictionary dictionaryWithCapacity:20];
[combinedAttributes addEntriesFromDictionary:areaAttributes];
[combinedAttributes addEntriesFromDictionary:gpsAttributes];
(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 struct
within the guts of some library, but externally looks just like an NSDictionary
. (A similar purpose to, e.g., namedtuple
in Python.) The methods that return them are all designed to return NSDictionary
, not NSMutableDictionary
, so nobody's going to know they have a restricted set of keys. Except, of course, if they call mutableCopy
.
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 mutableCopy
and shallow-copy it into a dictionary of a type you control.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With