Chapter 27: Building a (Core) Foundation

As an iOS developer, you will spend most of your time using the UIKit and Foundation frameworks. UIKit provides user interface elements like UIView and UIButton. Foundation provides basic data structures like NSArray and NSDictionary. These can handle the vast majority of problems the average iOS application will encounter. But some things require lower-level frameworks. The names of these lower-level frameworks often start with the word “Core,” Core Text, Core Graphics, and Core Video, for example. What they all have in common are C-based APIs based on Core Foundation.

Core Foundation provides a C API that is similar to the Objective-C Foundation framework. It provides a consistent object model with reference counting and containers, just like Foundation, and simplifies passing common data types to low-level frameworks. As you see later in this chapter, Core Foundation is tightly coupled with Foundation, making it easy to pass data between C and Objective-C.

In this chapter, you find out about the Core Foundation data types and naming conventions. You learn about Core Foundation allocators and how they provide greater flexibility than +alloc provides in Objective-C. This chapter covers Core Foundation string and binary data types extensively. You discover Core Foundation collection types, which are more flexible than their Foundation counterparts and include some not found in Objective-C. Finally, you learn how to move data easily between Core Foundation and Objective-C using toll-free bridging. When you’re finished, you will have the tools you need to use the powerful Core frameworks, as well as more flexible data structures to improve your own projects.

All code samples in this chapter can be found in main.m and MYStringConversion.c in the online files for this chapter.

Core Foundation Types

Core Foundation is made up primarily of opaque types, which are simply C structs. Opaque types are similar to classes in that they provide encapsulation, and some inheritance and polymorphism. The similarity should not be overstated, however. Core Foundation is implemented in pure C, where there is no language support for inheritance or polymorphism, so sometimes the class metaphor can become strained. But for general usage, Core Foundation can be thought of as an object model with CFType as its root “class.”

Like Objective-C, Core Foundation deals with pointers to instances. In Core Foundation, these pointers are given the suffix Ref. For example, a pointer to a CFType is a CFTypeRef, and a pointer to a string is a CFStringRef. Mutable versions of opaque types include the word Mutable, so a CFMutableStringRef is the mutable form of a CFStringRef. Generally, mutable types can be treated as though they were a subclass of the nonmutable type, just as in Foundation. For simplicity, and to match the Apple documentation, this chapter uses the term CFString to refer to the thing a CFStringRef points to, even though Core Foundation does not define the symbol CFString.

Because Core Foundation is implemented in C, and C has no language support for inheritance or polymorphism, how does Core Foundation give the illusion of an object hierarchy? First, CFTypeRef is just a void*. This provides a crude kind of polymorphism because it allows arbitrary types to be passed to certain functions, particularly CFCopyDescription, CFEqual, CFHash, CFRelease, and CFRetain.

Except for CFTypeRef, opaque types are structs. A mutable and immutable pair is usually of the form

typedef const struct __CFString * CFStringRef;

typedef struct __CFString * CFMutableStringRef;

This way, the compiler can enforce const correctness to provide a kind of inheritance. It should be clear that this isn’t real inheritance. There is no good way to provide arbitrary subclasses of CFString that the compiler will type-check. For example, consider the following code:

  CFStringRef errName = CFSTR(“error”);

  CFErrorRef error = CFErrorCreate(NULL, errName, 0, NULL);

  CFPropertyListRef propertyList = error;

A CFError is not a CFPropertyList, so line 3 should generate a warning. It doesn’t because CFPropertyListRef is defined as CFTypeRef, which is required because it has several “subclasses,” including CFString, CFDate, and CFNumber. Once something has several subclasses, it generally has to be treated as a void* (CFTypeRef) in Core Foundation. This isn’t obvious from looking at the code, but luckily it doesn’t come up that often. Most types are defined as a specific struct or const struct.

Naming and Memory Management

As in Cocoa, naming conventions are critical in Core Foundation. The most important rule is the Create Rule: If a function has the word Create or Copy in its name, you are an owner of the resulting object and must eventually release your ownership using CFRelease. Like Cocoa, objects are reference counted and can have multiple “owners.” When the last owner calls CFRelease, the object is destroyed.

There is no equivalent of NSAutoreleasePool in Core Foundation, so functions with Copy in the name are much more common than in Cocoa. Some functions, however, return a reference to an internal data structure or to a constant object. These functions generally include the word Get in their name (the Get Rule). The caller is not an owner and does not need to release them.

Get in Core Foundation is not the same as get in Cocoa. Core Foundation functions including Get return an opaque type or a C type. Cocoa methods that begin with get update a pointer passed by reference.

There is no automatic reference counting in Core Foundation. Memory management in Core Foundation is very similar to manual memory management in Cocoa:

If you Create or Copy an object, you are an owner.

If you do not Create or Copy an object, you are not an owner. If you want to prevent the object from being destroyed, you must become an owner by calling CFRetain.

If you are an owner of an object, you must call CFRelease when you’re done with it.

CFRelease is very similar to release in Objective-C, but there are important differences. The most critical is that you cannot call CFRelease(NULL). This is somewhat unfortunate, and many specialized versions of CFRelease exist that do allow you to pass NULL (CGGradientRelease, for instance). CFRelease also behaves differently than retain in garbage-collected environments. This doesn’t apply to iOS, so it isn’t covered here, but you can read Apple’s Memory Management Programming Guide for Core Foundation for more information.

Some functions have both Create and Copy in their name. For example, CFStringCreateCopy creates a copy of another CFString. Why not just CFStringCopy? That’s because Create tells you other things about the function than just the ownership rule. It indicates that the first parameter is a CFAllocatorRef, which lets you customize how the newly created object is allocated. In almost all cases, you pass NULL for this parameter, which specifies the default allocator: kCFAllocatorDefault. (I’ll cover allocators in more depth in a moment.) Knowing that the function is a creator, the name also tells you that it makes a copy of the passed string.

Conversely, a function with NoCopy in its name does not make a copy. For example, CFStringCreateWithBytesNoCopy takes a pointer to a buffer and creates a string without copying the bytes. So who is now responsible for releasing the buffer? That brings us back to allocators.

Allocators

A CFAllocatorRef is a strategy for allocating and freeing memory. In almost all cases, you want the default allocator, kCFAllocatorDefault, which is the same as passing NULL. This allocates and frees memory in “the normal way” according to Core Foundation. This way is subject to change, and you shouldn’t rely on any particular behavior. It is rare to need a specialized allocator, but in a few cases, it can be useful. Here are the standard allocators to give an idea of what they can do:

kCFAllocatorDefault—The default allocator. It is equivalent to passing NULL.

kCFAllocatorSystemDefault—The original default system allocator. This is available in case you have changed the default allocator using CFAllocatorSetDefault. This is very rarely necessary.

kCFAllocatorMalloc—Calls malloc, realloc, and free. This is particularly useful as a deallocator for CFData and CFString if you created the memory with malloc.

kCFAllocatorMallocZone—Creates and frees memory in the default malloc zone. This can be useful with garbage collection on the Mac, but is almost never useful in iOS.

kCFAllocatorNull—Does nothing. Like kCFAllocatorMalloc, this can be useful with CFData or CFString as a deallocator if you don’t want to free the memory.

kCFAllocatorUseContext—Only used by the CFAllocatorCreate function. When you create a CFAllocator, the system needs to allocate memory. Like all other Create methods, this requires an allocator. This special allocator tells CFAllocatorCreate to use the functions passed to it to allocate the CFAllocator.

See the later section “Backing Storage for Strings,” for examples of how these can be used in a practical problem.

Introspection

Core Foundation allows a variety of type introspections, primarily for debugging purposes. The most fundamental one is the CFTypeID, which uniquely identifies the opaque type of the object, similar to Class in Objective-C. You can determine the type of a Core Foundation instance by calling CFGetTypeID. The returned value is opaque and subject to change between versions of iOS. You can compare the CFTypeID of two instances, but most often you compare the result of CFGetTypeID to the value from a function like CFArrayGetTypeID. All opaque types have a related GetTypeID function.

As in Cocoa, Core Foundation instances have a description for debugging purposes, returned by CFCopyDescription. This returns a CFString that you’re responsible for releasing. CFCopyTypeIDDescription provides a similar string that describes a CFTypeID. Don’t rely on the format or content of these because they’re subject to change.

To write debugging output to the console, use CFShow. It will display the value of a CFString, or the description of other types. To display the description of a CFString, use CFShowStr. For example, given the following definitions

  CFStringRef string = CFSTR(“Hello”);

  CFArrayRef array = CFArrayCreate(NULL, (const void**)&string, 1,

                                   &kCFTypeArrayCallBacks);

here are the results for each kind of CFShow call:

  CFShow(array);

  <CFArray 0x6d47850 [0x1445b38]>{type = immutable, count = 1,

     values = (

       0 : <CFString 0x410c [0x1445b38]>{contents = “Hello”}

    )}

  CFShow(string);

  Hello

  CFShowStr(string);

  Length 5

  IsEightBit 1

  HasLengthByte 0

  HasNullByte 1

  InlineContents 0

  Allocator SystemDefault

  Mutable 0

  Contents 0x3ba7

Strings and Data

CFString is a Unicode-based storage container that provides rich and efficient functionality for manipulating, searching, and converting international strings. Closely related are the CFCharacterSet and CFAttributedString classes. CFCharacterSet represents a set of characters for efficiently searching, including or excluding certain characters from a string. CFAttributedString combines a string with ranges of attributes. This is most commonly used to handle rich text, but can be used for a variety of metadata storage.

CFString is closely related to NSString, and they are generally interchangeable, as you’ll see in “Toll-Free Bridging” section later in this chapter. This section focuses on the differences between CFString and NSString.

Constant Strings

In Cocoa, a literal NSString is indicated by an ampersand, as in @”string”. In Core Foundation, a literal CFString is indicated by the macro CFSTR, as in CFSTR(“string”). If you’re using the Apple-provided gcc and the option -fconstant-cfstrings, this macro uses a special built-in compiler hook that creates constant CFString objects at compile time. Clang also has this built-in compiler hook. If you’re using standard gcc, then an explicit CFStringMakeConstantString function is used to create these objects at runtime.

Because CFSTR has neither Create nor Copy in its name, you don’t need to call CFRelease on the result. You may, however, call CFRetain normally if you like. If you do, you should balance it with CFRelease as usual. This allows you to treat constant strings in the same way as programmatically created strings.

Creating Strings

A common way to generate a CFString is from a C string. Here is an example:

const char *cstring = “Hello World!”;

CFStringRef string = CFStringCreateWithCString(NULL, cstring,

kCFStringEncodingUTF8);

CFShow(string);

CFRelease(string);

Although many developers are most familiar with NULL-terminated C strings, there are other ways to store strings, and understanding them can be useful in improving code efficiency. In network protocols, it can be very efficient to encode strings as a length value followed by a sequence of characters. If parsers are likely to need only a part of the packet, it’s faster to use length bytes to skip over the parts you don’t need than to read everything looking for NULL. If this length encoding is 1 byte long, then the buffer is a Pascal string and Core Foundation can use it directly, as shown in the following code:

  // A common type of network buffer

  struct NetworkBuffer {

    UInt8 length;

    UInt8 data[];

  };

  

  // Some data we pulled off of the network into the buffer

  static struct NetworkBuffer buffer = {

    4, {‘T’, ‘e’, ‘x’, ‘t’}};

  

  CFStringRef string =

    CFStringCreateWithPascalString(NULL,

                                 (ConstStr255Param)&buffer,

                                   kCFStringEncodingUTF8);

  CFShow(string);

  CFRelease(string);

If you have length some other way, or if the length is not 1 byte long, you can use CFStringCreateWithBytes similarly:

CFStringRef string = CFStringCreateWithBytes(NULL,

                                             buffer.data,

                                             buffer.length,

                                     kCFStringEncodingUTF8,

                                             false);

The final false indicates this string does not have a byte order mark (BOM) at the beginning. The BOM indicates whether the string was generated on a big endian or little endian system. A BOM is not needed or recommended for UTF-8 encodings. This is one of many reasons to choose UTF-8 when possible.

Core Foundation constants begin with a k, unlike their Cocoa counterparts. For example, the Core Foundation counterpart to NSUTF8StringEncoding is kCFStringEncodingUTF8.

Converting to C Strings

Although converting from C strings is very simple, converting back to C strings can be deceptively difficult. There are two ways to get a C string out of a CFString: Request the pointer to the internal C string representation or copy the bytes out into your own buffer.

Obviously, the easiest and fastest way to get the C string is to request the internal C string pointer:

const char *

cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);

This appears to be the best of all worlds. It’s extremely fast, and you don’t have to allocate or free memory. Unfortunately, it may not work, depending on how the string is currently encoded inside of the CFString. If there isn’t an internal C string representation available, then this routine returns NULL and you have to use CFStringGetCString and pass your own buffer, although it isn’t obvious how large a buffer you need. Here’s an example of how to solve this problem:

char * MYCFStringCopyUTF8String(CFStringRef aString) {

  if (aString == NULL) {

    return NULL;

  }

  

  CFIndex length = CFStringGetLength(aString);

  CFIndex maxSize =

    CFStringGetMaximumSizeForEncoding(length,

                                      kCFStringEncodingUTF8);

  char *buffer = (char *)malloc(maxSize);

  if (CFStringGetCString(aString, buffer, maxSize,

                         kCFStringEncodingUTF8)) {

    return buffer;

  }

  return NULL;

}

...

CFStringRef string = CFSTR(“Hello”);

char * cstring = MYCFStringCopyUTF8String(string);

printf(“%s ”, cstring);

free(cstring);

MYCFStringCopyUTF8String is not the fastest way to convert a CFString to a C string because it allocates a new buffer for every conversion, but it’s easy to use and quick enough for many problems. If you’re converting a lot of strings and want to improve speed and minimize memory churn, you might use a function like this one that supports reusing a common buffer:

#import <malloc/malloc.h> // For malloc_size()

const char * MYCFStringGetUTF8String(CFStringRef aString,

                                     char **buffer) {

  if (aString == NULL) {

    return NULL;

  }

  

  const char *cstr = CFStringGetCStringPtr(aString,

                                    kCFStringEncodingUTF8);

  if (cstr == NULL) {

    CFIndex length = CFStringGetLength(aString);

    CFIndex maxSize =

      CFStringGetMaximumSizeForEncoding(length,

                 kCFStringEncodingUTF8) + 1; // +1 for NULL

    if (maxSize > malloc_size(buffer)) {

      *buffer = realloc(*buffer, maxSize);

    }

    if (CFStringGetCString(aString, *buffer, maxSize,

                           kCFStringEncodingUTF8)) {

      cstr = *buffer;

    }

  }

  return cstr;

}

The caller of MYCFStringGetUTF8String is responsible for passing a reusable buffer. The buffer may point to NULL or to preallocated memory. Keep in mind that the returned C string points into either the CFString or into buffer, so invalidating either of those can cause the returned C string to become invalid. In particular, passing the same buffer repeatedly to this function may invalidate old results. That’s the trade-off for its speed. Here’s how it would be used:

CFStringRef strings[3] = { CFSTR(“One”), CFSTR(“Two”),

                           CFSTR(“Three”) };

char * buffer = NULL;

const char * cstring = NULL;

for (unsigned i = 0; i < 3; ++i) {

  cstring = MYCFStringGetUTF8String(strings[i], &buffer);

         printf(“%s ”, cstring);    

}

free(buffer);

If you need conversion to be as fast as possible, and you know the maximum string length, then the following is even faster:

CFStringRef string = ...;

const CFIndex kBufferSize = 1024;

char buffer[kBufferSize];

CFStringEncoding encoding = kCFStringEncodingUTF8;

const char *cstring;

cstring = CFStringGetCStringPtr(string, encoding);

if (cstring == NULL) {

  if (CFStringGetCString(string, buffer, kBufferSize,

                         encoding)) {

    cstring = buffer;

  }

}

printf(“%s ”, cstring);

Because this approach relies on a stack variable (buffer), it’s difficult to wrap this into a simple function call, but it avoids any extra memory allocations.

Other String Operations

To developers familiar with NSString, most of CFString should be fairly obvious. You can find ranges of characters, append, trim and replace characters, compare, search, and sort as in Cocoa. CFStringCreateWithFormat provides functionality identical to stringWithFormat:. I won’t explore all the functions here. You can find them all in the documentation for CFString and CFMutableString.

Backing Storage for Strings

Generally a CFString will allocate the required memory to store its characters. This memory is called the backing storage. If you have an existing buffer, it’s sometimes more efficient or convenient to continue using it rather than copying all the bytes into a new CFString. You might do this because you have a buffer of bytes you want to convert into a string or because you want to continue to have access to the raw bytes while also using convenient string functions.

In the first case, where you already have a buffer, you generally use a function like CFStringCreateWithBytesNoCopy.

const char *cstr = “Hello”;

char *bytes = CFAllocatorAllocate(kCFAllocatorDefault,

                                  strlen(cstr) + 1, 0);

strcpy(bytes, cstr);

CFStringRef str =

  CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,

                                  bytes,

                                  kCFStringEncodingUTF8,

                                  kCFAllocatorDefault);

CFShow(str);

CFRelease(str);

Because you passed the default allocator (kCFAllocatorDefault) as the destructor, the CFString owns the buffer and will free it when it’s done using the default allocator. This matches the earlier call to CFAllocatorAllocate. If you were to allocate the buffer with malloc, the code would look like this:

const char *cstr = “Hello”;

char *bytes = malloc(strlen(cstr) + 1);

strcpy(bytes, cstr);

CFStringRef str =

  CFStringCreateWithCStringNoCopy(NULL, bytes,

                                  kCFStringEncodingUTF8,

                                  kCFAllocatorMalloc);

CFShow(str);

CFRelease(str);

In both cases, the allocated buffer is freed when the string is destroyed. But what if you wanted to keep the buffer for other uses? Consider the following code:

const char *cstr = “Hello”;

char *bytes = malloc(strlen(cstr) + 1);

strcpy(bytes, cstr);

CFStringRef str =

  CFStringCreateWithCStringNoCopy(NULL, bytes,

                                  kCFStringEncodingUTF8,

                                  kCFAllocatorNull);

CFShow(str);

CFRelease(str);

printf(“%s ”, bytes);

free(bytes);

You pass kCFAllocatorNull as the destructor. You still release the string because you created it with a Create function. But now the buffer pointed to by bytes is still valid after the call to CFRelease. You are responsible for calling free on bytes when you’re done with the buffer.

There is no guarantee that the buffer you pass will be the actual buffer used. Core Foundation may call the deallocator at any time and create its own internal buffer. Most critically, you must not modify the buffer after creating the string. If you have a buffer that you want to access as a CFString while allowing changes to it, then you need to use CFStringCreateMutableWithExternalCharactersNoCopy. This creates a mutable string that always uses the provided buffer as its backing store. If you change the buffer, you need to let the string know by calling CFStringSetExternalCharactersNoCopy. Using these functions bypasses many string optimizations, so use them with care.

CFData

CFData is the Core Foundation equivalent to NSData. It is much like CFString with similar creation functions, backing store management, and access functions. The primary difference is that CFData does not manage encodings like CFString. You can find the full list of functions in the CFData and CFMutableData references.

Collections

Core Foundation provides a rich set of object collection types. Most have Cocoa counterparts like CFArray and NSArray. There are a few specialized Core Foundation collections such as CFTree that have no Cocoa counterpart. Core Foundation collections provide greater flexibility in how they manage their contents. In this section, you learn about the Core Foundation collections that have Objective-C equivalents: CFArray, CFDictionary, CFSet, and CFBag. The other Core Foundation collections are seldom used, but I will introduce them so that you’re aware of what’s available if you need it.

Cocoa collections can hold only Objective-C objects and must retain them. Core Foundation collections can hold anything that can fit in the size of a pointer (32 bits for the ARM processor) and can perform any action when adding or removing items. The default behavior is very similar to the Cocoa equivalents, and Core Foundation collections generally retain and release instances when adding and removing. Core Foundation uses a structure of function pointers that defines how to treat items in the collection. Configuring these callbacks allows you to highly customize your collection. You can store nonobjects like integers, create weak collections that do not retain their objects, or modify how objects are compared for equality. The “Callbacks” section, later in this chapter, covers this topic. Each collection type has a default set of callbacks defined in the header. For example, the default callbacks for CFArray are kCFTypeArrayCallBacks. While introducing the major collections, I will focus on these default behaviors.

CFArray

CFArray corresponds to NSArray and holds an ordered list of items. Creating a CFArray takes an allocator, a series of values, and a set of callbacks, as shown in the following code.

CFStringRef strings[3] =

  { CFSTR(“One”), CFSTR(“Two”), CFSTR(“Three”) };

CFArrayRef array = CFArrayCreate(NULL, (void *)strings, 3,

                                 &kCFTypeArrayCallBacks);

CFShow(array);

CFRelease(array);

Creating a CFMutableArray takes an allocator, a size, and a set of callbacks. Unlike NSMutableArray capacity, which is only an initial size, the size passed to CFMutableArray is a fixed maximum. To allocate an array that can grow, pass a size of 0.

CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0,

                            &kCFTypeArrayCallBacks);

CFDictionary

CFDictionary corresponds to NSDictionary and holds key-value pairs. Creating a CFDictionary takes an allocator, a series of keys, a series of values, a set of callbacks for the keys, and a set of callbacks for the values.

  #define kCount 3

  CFStringRef keys[kCount] =

    { CFSTR(“One”), CFSTR(“Two”), CFSTR(“Three”) };

  CFStringRef values[kCount] =

    { CFSTR(“Foo”), CFSTR(“Bar”), CFSTR(“Baz”) };

  CFDictionaryRef dict =

    CFDictionaryCreate(NULL,

                       (void *)keys,

                       (void *)values,

                       kCount,

                       &kCFTypeDictionaryKeyCallBacks,

                       &kCFTypeDictionaryValueCallBacks);

Creating a CFMutableDictionary is like creating a CFMutableArray, except there are separate callbacks for the keys and values. As with CFMutableArray, the size is fixed if given. For a dictionary that can grow, pass a size of 0.

CFSet, CFBag

CFSet corresponds to NSSet and is an unordered collection of unique objects. CFBag corresponds to NSCountedSet and allows duplicate objects. As with their Cocoa counterparts, uniqueness is defined by equality. The function that determines equality is one of the callbacks.

Like CFDictionary, CFSet and CFBag can hold NULL values by passing NULL as their callback structure pointer.

Other Collections

Core Foundation includes several collections that do not have a Cocoa counterpart:

CFTree provides a convenient way to manage tree structures that might otherwise be stored less efficiently in a CFDictionary. There is a short example of CFTree in the section “Toll-Free Bridging,” later in the chapter.

CFBinaryHeap provides a binary-searchable container, similar to a sorted queue.

CFBitVector provides a convenient way to store bit values.

Full information on CFTree is available in Apple’s Collections Programming Topics for Core Foundation. See the Apple documentation on CFBinaryHeap and CFBitVector for more information on their usage. These are not often used and aren’t heavily documented.

Callbacks

Core Foundation uses a structure of function pointers that define how to treat items in the collection. The structure includes the following members:

retain—Called when an item is added to the collection. The default behavior is similar to CFRetain (you’ll learn what “similar” means in a moment). If it’s NULL, no action is performed.

release—Called when an item is removed from the collection, and when the collection is destroyed. The default behavior is similar to CFRelease. If it is NULL, no action is performed.

copyDescription—Called for each object in response to functions that want a human-readable description for the entire collection, such as CFShow or CFCopyDescription. The default value is CFCopyDescription. If this is NULL, the collection has some built-in logic to construct a simple description.

equal—Called to compare a collection object with another object to determine if they’re equal. The default value is CFEqual. If this is NULL, the collection will use strict equality (==) of the values. If the items are pointers to objects (as is the usual case), then this means that objects are only equal to themselves.

hash—This only applies to hashing collections like dictionaries and sets. This function is used to determine the hash value of an object. A hash is a fast way to compare objects. Given an object, a hash function returns an integer such that if two objects are equal, then their hashes are equal. This allows the collection to quickly determine unequal objects with a simple integer comparison, saving the expensive call to CFEqual for objects that are possibly equal. The default value is CFHash. If this is NULL, the value (usually a pointer) is used as its own hash.

The default values for retain and release act like CFRetain and CFRelease, but are actually pointers to the private functions __CFTypeCollectionRetain and __CFTypeCollectionRelease. The retain and release function pointers include the collection’s allocator in case you want to create a new object rather than retain an existing one. This is incompatible with CFRetain and CFRelease, which do not take an allocator. Usually this doesn’t matter because, in most cases, you’ll either leave retain and release as default or set them to NULL.

Each collection type has a default set of callbacks defined in the header. For example, the default callbacks for CFArray are kCFTypeArrayCallBacks. These can be used to easily modify default behavior. The following creates a nonretaining array, which could also hold nonobjects such as integers:

CFArrayCallBacks nrCallbacks = kCFTypeArrayCallBacks;

nrCallbacks.retain = NULL;

nrCallbacks.release = NULL;

CFMutableArrayRef nrArray = CFArrayCreateMutable(NULL, 0,

                              &nrCallbacks);

CFStringRef string =

  CFStringCreateWithCString(NULL, “Stuff”,

                            kCFStringEncodingUTF8);

CFArrayAppendValue(nrArray, string);

CFRelease(nrArray);

CFRelease(string);

Another example of callback configuration is to allow NULL values or keys. Dictionaries, sets, and bags can hold NULL values or keys if the retain and release callbacks are NULL (this also makes them nonretaining). These types have CFTypeGetValueIfPresent functions to handle this case. For example, the function CFDictionaryGetValueIfPresent() allows you to determine whether the value was NULL versus missing, as shown in the following code:

CFDictionaryKeyCallBacks cb = kCFTypeDictionaryKeyCallBacks;

cb.retain = NULL;

cb.release = NULL;

CFMutableDictionaryRef dict =

  CFDictionaryCreateMutable(NULL, 0, &cb,

                            &kCFTypeDictionaryValueCallBacks);

CFDictionarySetValue(dict, NULL, CFSTR(“Foo”));

const void *value;

Boolean fooPresent =

  CFDictionaryGetValueIfPresent(dict, NULL, &value);

CFRelease(dict);

Other collections, such as CFArray, cannot hold NULL values. As in Foundation, you must use a special placeholder NULL constant called kCFNull. It is an opaque type (CFNull), so it can be retained and released.

Core Foundation collections are much more flexible than their Cocoa equivalents. As you see in the next section, however, you can bring this flexibility almost transparently to Cocoa through the power of toll-free bridging.

Toll-Free Bridging

One of the cleverest aspects of Core Foundation is its capability to transparently exchange data with Foundation. For example, any function or method that accepts an NSArray also accepts a CFArray with only a bridge cast. A bridge cast is an instruction to the compiler about how to apply automatic reference counting.

In many cases, you only need to use the __bridge modifier, as shown in the following code:

NSArray *nsArray = [NSArray arrayWithObject:@”Foo”];

printf(“%ld ”, CFArrayGetCount((__bridge CFArrayRef)nsArray));

This essentially tells the compiler to do nothing special. It should simply cast nsArray as a CFArrayRef and pass it to CFArrayGetCount. There is no change to the reference count of nsArray.

This works in reverse as well:

CFMutableArrayRef cfArray =

  CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

CFArrayAppendValue(cfArray, CFSTR(“Foo”));

NSLog(@”%ld”, [(__bridge id)cfArray count]);

CFRelease(cfArray);

The __bridge cast works as long as there is no Core Foundation memory management involved. In the preceding examples, you aren’t assigning the results to variables or returning them. Consider this case, however:

- (NSString *)firstName {

  CFStringRef cfString = CFStringCreate...;

  return (???)cfString;

}

How can you cast cfString correctly? Before ARC, you would have cast this to an NSString and called autorelease. With ARC, you can’t call autorelease, and ARC doesn’t know that cfString has an extra retain on it from CFStringCreate.... You again use a bridge cast, this time in the form of a function as in this example:

  return CFBridgingRelease(cfString);

This function transfers ownership from Core Foundation to ARC. In the process, it reduces the retain count by one to balance the CFStringCreate.... You must use a bridge cast to achieve this. Calling CFRelease before returning the object would destroy the object.

When transferring an object from ARC to Core Foundation, you use CFBridgingRetain, which increases the retain count by one, as shown in the following code:

CFStringRef cfStr = CFBridgingRetain([nsString copy]);

nsString = nil; // Ownership now belongs to cfStr

...

CFRelease(cfStr);

The bridging functions can also be written in a typecast style as follows:

NSString *nsString = CFBridgingRelease(cfString);

NSString *nsString = (__bridge_transfer id)cfString;

CFStringRef cfString = CFBridgingRetain(nsString);

CFStringRef cfString = (__bridge_retained CFTypeRef)nsString;

CFTypeRef is a generic pointer to a Core Foundation object, and id is a generic pointer to an Objective-C object. You could also use explicit types here like CFStringRef and NSString*.

The function form is shorter and, in my opinion, easier to understand. CFBridgingRelease and CFBridgingRetain should be used only when an object is being transferred between ARC and Core Foundation. They’re not replacements for CFRetain or CFRelease or a way to “trick” the compiler into adding an extra retain or release on Objective-C objects. Once an object is transferred from Core Foundation to ARC, the Core Foundation variable should not be used again and should be set to NULL. Conversely, when converting from ARC to Core Foundation, you need to immediately set the ARC variable to nil. You have transferred ownership from one variable to another, so the old variable needs to be treated as invalid.

Not only is toll-free bridging very convenient for moving information between C and Objective-C, it also enables Cocoa developers to make use of certain Core Foundation functions that have no Objective-C equivalent. For example, CFURLCreateStringByAddingPercentEscapes allows much more powerful transformations than the equivalent NSURL stringByAddingPercentEscapesUsingEncoding:.

Even types that aren’t explicitly toll-free bridged are still bridged to NSObject. This means that you can store Core Foundation objects (even ones with no Cocoa equivalent) in Cocoa collections, as shown in this example:

CFTreeContext ctx = {0, (void*)CFSTR(“Info”), CFRetain,

                     CFRelease, CFCopyDescription};

CFTreeRef tree = CFTreeCreate(NULL, &ctx);

NSArray *array = [NSArray arrayWithObject:(__bridge id)tree];

CFRelease(tree);

NSLog(@”%@”, array);

Toll-free bridging is implemented in a fairly straightforward way. Every Objective-C object structure begins with an ISA pointer to a Class:

typedef struct objc_class *Class;

typedef struct objc_object {

  Class isa;

} *id;

Core Foundation opaque types begin with a CFRuntimeBase, and the first element of that is also an ISA pointer:

typedef struct __CFRuntimeBase {

  uintptr_t _cfisa;

  uint8_t _cfinfo[4];

#if __LP64__

  uint32_t _rc;

#endif

} CFRuntimeBase;

_cfisa points to the toll-free bridged Cocoa class. These are subclasses of the equivalent Cocoa class; they forward Objective-C method calls to the equivalent Core Foundation function call. For instance, CFString is bridged to the private toll-free bridging class NSCFString.

If there is no explicit bridging class, then _cfisa points to __NSCFType, which is a subclass of NSObject, and forwards calls like retain and release.

To handle Objective-C classes passed to Core Foundation functions, all public toll-free functions look something like this:

CFIndex CFStringGetLength(CFStringRef str) {

  CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFIndex, str, “length”);

  __CFAssertIsString(str);

  return __CFStrLength(str);

}

CF_OBJC_FUNCDISPATCH0 checks the _cfisa pointer. If it matches the Core Foundation bridging class for the given CFTypeID, then it passes the call along to the real Core Foundation function. Otherwise, it translates the call into an Objective-C message (length in this case, given as a C string).

Summary

Core Foundation bridges the gap between C and Objective-C code, providing powerful data structures for C and near-transparent data passing to and from low-level code. As Apple releases more low-level Core frameworks that require these types, Core Foundation is an increasingly important part of an iOS developer’s toolkit.

Core Foundation data structures are generally more flexible than their Cocoa equivalents. They provide better control over how memory is managed through allocators and often include functions for more specialized problems like handling Pascal strings or very configurable URL percent substitutions. Core Foundation collections can be configured to be nonretaining and can even store nonobjects such as integers.

Although Objective-C is extremely powerful, you can still generally write code that is faster and more efficient in pure C, which is why the lowest-level APIs are all C APIs. For those parts of your programs that require the kind of performance you can only get from C, Core Foundation provides an excellent collection of abstract data types that you can easily exchange with the higher-level parts of your program. The vast majority of problems in iOS are best solved in Cocoa and Objective-C, but for those places that C is appropriate, Core Foundation is a powerful tool.

Further Reading

Apple Documentation

The following documents are available in the iOS Developer Library at developer.apple.com or through the Xcode Documentation and API Reference.

Collections Programming Topics for Core Foundation

Core Foundation Design Concepts

Data Formatting Guide for Core Foundation

Dates and Times Programming Guide for Core Foundation

Programming With ARC Release Notes: “Managing Toll-Free Bridging”

Memory Management Programming Guide for Core Foundation

Property List Programming Topics for Core Foundation

Strings Programming Guide for Core Foundation

Other Resources

Clang Documentation. “Automatic Reference Counting”clang.llvm.org/docs/AutomaticReferenceCounting.html

ridiculous_fish, “Bridge.” An entertaining introduction to toll-free bridging internals by one of the AppKit and Foundation team at Apple.ridiculousfish.com/blog/posts/bridge.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.122.235