Reputation: 2746
I'm trying to figure out how to create an NSMutableDictionary that retains instead of copies its keys. I have implemented -(NSUInteger)hash and -(id)isEqual: for my desired keys, I am just having trouble figuring out which options to specify in the callbacks.
CFDictionaryKeyCallBacks keyCallbacks = { 0, NULL, NULL, CFCopyDescription, CFEqual, NULL };
self.commonParents = (NSMutableDictionary*)CFBridgingRelease(CFDictionaryCreateMutable(nil, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks));
The above code works correctly in ARC for using weak references to keys, but what if I want strong references? What should the key callbacks look like?
Upvotes: 2
Views: 1123
Reputation: 2276
I think the best answer is buried in comment, so I'll highlight it here: The simplest approach is to use a +[NSMapTable strongToStrongObjectsMapTable]
(or maybe one of the variants with weak
references).
Upvotes: 1
Reputation: 243156
tl;dr:
Create a CFDictionaryRef
with the provided default callback functions. It'll do what you want. Just don't call it an NSDictionary
.
Yes, you can create a CFDictionaryRef
that retains its keys and does not copy them. This is, in fact, the default behavior of a CFDictionaryRef
.
The documentation for CFDictionaryCreateMutable()
says:
If the dictionary will contain only CFType objects, then pass a pointer to
kCFTypeDictionaryKeyCallBacks
as this parameter to use the default callback functions.
(So if you're only going to be putting normal Objective-C objects into the array and not random things like void *
pointers or whatever, this is what you want)
And the documentation for kCFTypeDictionaryKeyCallBacks
says:
Predefined
CFDictionaryKeyCallBacks
structure containing a set of callbacks appropriate for use when the keys of a CFDictionary are all CFType-derived objects. The retain callback isCFRetain
, the release callback isCFRelease
, the copy callback isCFCopyDescription
, the equal callback isCFEqual
. Therefore, if you use a pointer to this constant when creating the dictionary, keys are automatically retained when added to the collection, and released when removed from the collection.
Note that the retain
callback is CFRetain()
and not something like CFCopyObject
(which doesn't actually exist).
In fact, Core Foundation doesn't have a canonical way to "copy any object", which is why functions like CFStringCreateCopy
, CFArrayCreateCopy
, CGPathCreateCopy
, etc exist.
So, what you can do is create your dictionary like this:
CFDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
And you now have a dictionary that retains its keys and does not copy them.
I'm going to put the following bit in big letters so that you grok what I'm about to say:
NSDictionary
.Yes, NSDictionary
and CFDictionaryRef
are toll-free bridged. But casting this CFDictionaryRef
to an NSDictionary
would be an abuse of that bridging, because of this line in the NSDictionary
documentation:
...a key can be any object (provided that it conforms to the
NSCopying
protocol—see below)
Similarly, the documentation for -[NSMutableDictionary setObject:forKey:]
explicitly says:
The key is copied (using
copyWithZone:
; keys must conform to the NSCopying protocol).
The keys in your dictionary don't have to conform to <NSCopying>
and are not copied using -copyWithZone:
, which means your dictionary is NOT an NSDictionary
(or NSMutableDictionary
). Any time you see NSDictionary
used in code, you should be providing a key-value map where the keys are copied (not retained). That is the API contract. To do anything else could result in undefined behavior.
(The fact that some objects override -copy
to return [self retain]
is an implementation detail and is not relevant to this discussion on "what is an NSDictionary
".)
Upvotes: 5
Reputation: 8664
I think there is 2 possibles solutions that could be achieved using plain old NSMutableDictionary. They are not as elegant as NSMapTable would be.
You state that each of your Key have a uniqueID
, so I assume that this Value won't change over time.
Option 1 :
Use the uniqueID
of your actual key to be the key of an NSMutableDictionary that would store NSArray of @[key, value]
so the whole structure look like this
@{ key1.uniqueID : @[key1, value1], key2.uniqueID : @[key2 : value2] }
Option 2 :
Make a subclass of NSObject that is a wrapper around option 1. Or any variation on option 1.
Those are only valid if uniqueID
never change
Upvotes: 0
Reputation: 21966
My suggest is that instead of doing this you subclass NSString or whatever class you're using as key, and override the copy method in a way that it returns the string retained, instead of a copied string.
Upvotes: 0