13

I would like to iterate through a CFDictionary (CFPropertyList) and get all values on a specific level.

This would be my dictionary / property-list:


 root
 
  A
  
   foo
   0
   bar
   0
  
  B
  
   foo
   10
   bar
   100
  
  C
  
   foo
   20
   bar
   500
  
 

Using ObjC it would look something like this:

//dict is loaded with the dictionary below "root"
NSDictionary *dict = [...];
NSEnumerator *enumerator = [dict keyEnumerator];
NSString *key;
while (key = [enumerator nextObject]) 
{
    NSLog(key);
};

And it would print out a list of keys to the console like this:

A
B
C

How do you achieve this when using C/C++ on the CoreFoundation-level?

Till
  • 27,559
  • 13
  • 88
  • 122

3 Answers3

47

Use CFDictionaryApplyFunction to iterate through a dictionary.

static void printKeys (const void* key, const void* value, void* context) {
  CFShow(key);
}
...
CFDictionaryApplyFunction(dict, printKeys, NULL);
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
12

Based on code from SeeMyFriends:

CFDictionaryRef dict = CFDictionaryCreate(...)
size size = CFDictionaryGetCount(dict);
CFTypeRef *keysTypeRef = (CFTypeRef *) malloc( size * sizeof(CFTypeRef) );
CFDictionaryGetKeysAndValues(dict, (const void **) keysTypeRef, NULL);
const void **keys = (const void **) keysTypeRef;

You can now walk through the pointers in keys[]. Don't forget to free(keys) when you're done.

Remember that dictionary keys are not strings. They're void* (which is why they took the trouble of casting keysTypeRef into keys). Also note that I've only grabbed keys here, but you could also get values at the same time. See the SeeMyFriends code for a more detailed example.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • The CFDictionaryGetKeysAndValues method takes a pointer to a pointer to an array of the keys. Why, then, do you not pass the address of the "keysTypeRef" pointer (which would be a pointer to that pointer)? – erikprice Aug 19 '11 at 21:49
  • `CFDictionaryGetKeysAndValues` takes a a pointer to an array of pointers (`void **`), not a pointer to a pointer to an array of pointers (`void ***`). Remember, `CFTypeRef` itself is a pointer. – Rob Napier Aug 21 '11 at 21:38
  • Could you elaborate on this? That link is down. – chacham15 Apr 27 '13 at 01:34
  • See KennyTM's answer, which is an easier approach than doing this by hand. – Rob Napier Apr 27 '13 at 20:41
  • If you don't only want to print the values (or print them nicer), you might have to check for the type of every property. An example of that is in http://stackoverflow.com/a/17981965/1904815. – JonnyJD Jul 31 '13 at 22:22
4

CFCopyDescription is helpful when Debugging...

CFCopyDescription
Returns a textual description of a Core Foundation object.

    CFStringRef CFCopyDescription (
       CFTypeRef cf
    );
hewigovens
  • 1,190
  • 11
  • 9