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