Search code examples
cxcodemacoscore-foundation

Why Does CFStringCreateArrayBySeparatingStrings Does Not Create CFStringRef Value Items?


I am trying to develop a very simple Core Foundation program that uses the function CFStringCreateArrayBySeparatingStrings(). However, it does not work as I expect.

The program is the following:

#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>

void applier(const void *value, void *context) {
  printf("%s\n", CFStringGetCStringPtr((CFStringRef)value, CFStringGetSystemEncoding()));
}

int main(int argc, const char * argv[]) {
  CFStringRef stringToParse = CFSTR("John Pappas,Mary Foo,Peter Pan");
  CFStringRef separatorString = CFSTR(",");
  
  CFArrayRef array = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, 
                                                            stringToParse, 
                                                            separatorString);
  
  CFArrayApplyFunction(array, 
                       CFRangeMake((CFIndex)0, 
                                   (CFIndex)CFArrayGetCount(array)), 
                       applier, 
                       NULL);
  
  CFRelease(array);
  
  return 0;
}

When I run the program, the output is the following:

John Pappas
(null)
(null)
Program ended with exit code: 0

I don't understand why the 2nd and 3rd values are not printed as expected.

When trying to debug the program, before exiting, the debugger shows that the array does have 3 elements, but the types of its values are not the same type for all. Here is what I am looking at when I stop at the break point with the debugger:

enter image description here

Also, by looking at the debugger, I am a little bit surprised that the elements of the array have values of types that involve NS. I thought that I was using Core Foundation and not Foundation framework. So, I expected the values to have types like CFStringRef.

Can you help me make this program run successfully using Core Foundation API?


Solution

  • In response to a related question I dived into the documentation. Here's what I found:

    From Foundation Release Notes for OS X v10.10 and iOS v8

    NS/CFStrings now use a “tagged pointer” format where the string contents are stored in the pointer directly in some cases. This happens automatically thru the existing APIs that create strings, without need to use any new API.

    This change will break any code which treats NS/CFStrings objects as pointers

    Indeed, CFStringCreateArrayBySeparatingStrings returns NSTaggedPointerStrings.

    From the documentation of CFStringGetCStringPtr:

    This function either returns the requested pointer immediately, with no memory allocations and no copying, in constant time, or returns NULL. If the latter is the result, call an alternative function such as the CFStringGetCString(::::) function to extract the characters.

    Whether or not this function returns a valid pointer or NULL depends on many factors, all of which depend on how the string was created and its properties. In addition, the function result might change between different releases and on different platforms. So do not count on receiving a non-NULL result from this function under any circumstances.

    CFStringGetCStringPtr can't return a pointer for a NSTaggedPointerString. Use CFStringGetCString instead of CFStringGetCStringPtr or try CFStringGetCStringPtr and if the result is NULL then call CFStringGetCString.