Creating custom CFDictionary callbacks

I am trying to implement categories for NSMutableDictionary class with at least two methods: one for NSDictionary that retains (not copies) its keys, and one for NSDictionary that weak references its keys (i.e. does nothing to them).

Values are simply retained in both cases.

Thus, as CFDictionaryRef is said to be toll-free bridged with NSDictionary, I actually do the following:

+ (NSMutableDictionary *)dictionaryWithWeakReferencedKeysForCapacity: (NSUInteger)capacity
{   
    CFDictionaryKeyCallBacks    keyCallbacks    = { 0, NULL, NULL, CFCopyDescription, CFEqual, NULL }; 
    CFDictionaryValueCallBacks  valueCallbacks  = { 0, ___f_KBWS_DICTIONARY_RETAIN_CALLBACK, ___f_KBWS_DICTIONARY_RELEASE_CALLBACK, CFCopyDescription, CFEqual };

    return [(id)CFDictionaryCreateMutable(NULL, capacity, &keyCallbacks, &valueCallbacks) autorelease]; 
}

The second method (for retained keys) looks alike and is not presented here. Scary functions inside the code are:

static const void *___f_KBWS_DICTIONARY_RETAIN_CALLBACK(CFAllocatorRef allocator, const void *value)
{
    id object = (id)value;
    return [object retain];
};

static void ___f_KBWS_DICTIONARY_RELEASE_CALLBACK(CFAllocatorRef allocator, const void *value)
{
    id object = (id)value;
    return [object release];
};

I had to write these myself as soon as I haven't found standard core foundation callbacks for retaining and releasing keys.

I plan to use these categories for dictionaries that will store subclasses NSObjects only. The question is: are these valid callbacks for this case? Is there something wrong in my code besides that?

Answers


I don't see anything wrong with these callbacks, per se. That said, you should be careful about "handing out" NSMutableDictionaries to the "public" that don't behave like normal NSMutableDictionaries.

To elaborate, giving someone an NSMutableDictionary pointer is implicitly making statements about how that object behaves. Having set it up as a CFMutableDictionary, with non-standard callbacks, and casting it by the magic of Toll-Free Bridging compiles, but is setting the consumer up for failure.

More generally, I would say that you can do this with impunity as long as you keep the dictionary private to whatever class owns it. Your code snippet implies that you might be handing this out to "the rest of the world" under the guise of being an NSMutableDictionary, when that's not really the case.

If you've got to hand these instances out, I would recommend making your own custom NSMutableDictionary subclass like "MyWeakRefKeyMutableDictionary" and returning that type explicitly. Then at least if someone treats it like an NSMutableDictionary, they can't say they weren't warned.


I had to write these myself as soon as I haven't found standard core foundation callbacks for retaining and releasing keys.

I think you're after CFRetain and CFRelease. But if you only want to change the key callbacks you can use the standard kCFTypeDictionaryValueCallBacks.

+ (NSMutableDictionary *)dictionaryWithWeakReferencedKeysForCapacity: (NSUInteger)capacity
{   
    CFDictionaryKeyCallBacks    keyCallbacks    = { 0, NULL, NULL, CFCopyDescription, CFEqual, NULL }; 

    return [(id)CFDictionaryCreateMutable(NULL, capacity, &keyCallbacks, &kCFTypeDictionaryValueCallBacks) autorelease]; 
}

Need Your Help

Cannot install 32-bit app on Server 2008 R2 64-bit using asnet_regiis.exe

iis 64-bit

Currently I have a set of web applications that need to be installed (using IIS). They can only run in 32-bit mode so requires IIS to be set to run 32-bit apps. The steps that we've taken to do thi...

How to set choice field values in Symfony2?

php forms symfony

Whats the best way to set a collection of key/values pairs (obtained from MySQL) as a choice field 'choices' inside a controller?