Part III: Best - Core Foundation

At this point, if you know anything about object-oriented programming, you are thinking Yeah, but you would still have to create types like KFSet, KFArray, KFDictionary, KFString, etc. Well, that is true. The good news, however, is that Apple has done all that for us. Starting with their fundamental CFType, Apple has built CFSet, CFArray, CFDictionary, CFString, and many, many other wonderful types. Reference counting is also the approach employed in Core Foundation.

Even more wonderful is that Apple's Core Foundation framework is open source and a version called Core Foundation Lite can be used to write cross-platform applications for Mac OS X, Linux, and Windows (via Cygwin).

How do I make my own C type that inherits from CFType?

Simplest Solution : Use a CFDictionary and place all instance variables into dictionary.

This is easy if your instance variables (ivars) can be readily defined with other Core Foundation Types, e.g., CFNumber, CFString, etc. but adds overhead particularly when you have to wraps many int’s and float’s into CFNumbers.

Roll your own CFType

Alternatively, you can create your own type that inherits from CFType and takes full advantage of the Core Foundation library. Inside the source for our Shape is a structure that inherits from CFType.

#include <CoreFoundation/CoreFoundation.h>
#include "CFRuntime.h"

struct __CFShape {
    CFRuntimeBase   _base;

    // Shape Type attributes
    float xPosition;
    float yPosition;
    float orientation;
};

Notice that the first line of every CFType structure is CFRuntimeBase _base;. This is defined in the file CFRuntime.h, which can be found at the bottom of this page. This file needs to be included in your project if you plan to create your own CFType.

Inside the header file for CFShape we define the types.

typedef const struct __CFShape * CFShapeRef;
typedef struct __CFShape * CFMutableShapeRef;

Also inside the source we use the following code for allocating and creating a CFShape.

// Static variable that gets assigned a unique value when the first CFShape is created.
static CFTypeID _kCFShapeID = _kCFRuntimeNotATypeID;

// CFRuntimeClass variable defining our CFShape type for Core Foundation.
static CFRuntimeClass _kCFShapeClass = {
    0,
    "CFShape",
    NULL,
    NULL,
    CFShapeFinalize,
    CFShapeEqual,
    NULL,
    NULL,
    CFShapeCopyDebugDesc};

// Allocate CFShape.
static struct __CFShape *CFShapeAllocate()
{
    // First time a CFShape is allocated, so we need to register the type with Core Foundation
    if(_kCFShapeID ==_kCFRuntimeNotATypeID) {
        _kCFShapeID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&_kCFShapeClass);
    }

    // Allocate a CFShape
    struct __CFShape *theShape;
    uint32_t extra = sizeof(struct __CFShape) - sizeof(CFRuntimeBase);
    theShape = (struct __CFShape *)_CFRuntimeCreateInstance(kCFAllocatorDefault, _kCFShapeID, extra, NULL);
    if (NULL == theShape) return NULL;
    return theShape;
}

CFShapeRef CFShapeCreate(float xPosition, float yPosition, float orientation)
{
    struct __CFShape *newShape = CFShapeAllocate();
    if(NULL == newShape) return NULL;
    newShape->xPosition = xPosition;
    newShape->yPosition = yPosition;
    newShape->orientation = orientation;
    return newShape;
}

CFMutableShapeRef CFShapeCreateMutable(float xPosition, float yPosition, float orientation)
{
    return (CFMutableShapeRef) CFShapeCreate(xPosition, yPosition, orientation);
}

Usage of CFShape

Here's an example to match our previous examples.

    CFMutableShapeRef shape = CFShapeCreateMutable(0.0, 0.0, 0.0);
    CFShapeShow(shape);
    
    CFShapeTranslate(shape, 10.0, 20.0);
    CFShapeRotate(shape, 180.);
    CFShapeShow(shape);
    
    CFRelease(shape);

Things get more exciting, however, when you bring all the rest of Core Foundation's various types to bear in your code. Here's an example using CFArray.

    CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
    for(float orientation = 0.0; orientation <360.; orientation += 45.0) {
        CFMutableShapeRef shape = CFShapeCreateMutable(0.0, 0.0, orientation);
        CFShapeShow(shape);
        CFArrayAppendValue(array, shape);
        CFRelease(shape);
    }
    
    printf("\n\n");
    CFIndex count = CFArrayGetCount(array);
    for(CFIndex index = 0; index< count; index++) {
        CFMutableShapeRef shape = (CFMutableShapeRef) CFArrayGetValueAtIndex(array, index);
        CFShapeShow(shape);
    }
    
    CFRelease(array);

Just wait, it gets even better. You can even use your CFType as an object in Objective-C. Here's a translation of the code above into objective C.

    @autoreleasepool {
        
        NSMutableArray *array = [[NSMutableArray alloc] init];
        for(float orientation = 0.0; orientation <360.; orientation += 45.0) {
            CFMutableShapeRef shape = CFShapeCreateMutable(0.0, 0.0, orientation);
            CFShapeShow(shape);
            NSLog(@"retain count is %ld",[(id) shape retainCount]);
            [array addObject:(id) shape];
            NSLog(@"retain count is %ld",[(id) shape retainCount]);
            [(id) shape retain];
            NSLog(@"retain count is %ld",[(id) shape retainCount]);
            [(id) shape release];
            CFRelease(shape);
        }
        
        printf("\n\n");
        NSUInteger count = [array count];
        for(NSUInteger index = 0; index< count; index++) {
            CFMutableShapeRef shape = (CFMutableShapeRef) [array objectAtIndex:index];
            CFShapeShow(shape);
        }
        
        [array release];
    }

While we still create our shape with the C method, we can put our shape into an NSArray and get it out with no problems. We can retain and release our shape as if it was an objective C object. And our shape even gets released when the array containing it is released.

One final note: I would suggest that you don't use CF as the first two characters of the name of your own CFTypes. Leave those for Apple.

Source code used in this section:

CFRuntime.h
CFShape.c
CFShape.h
main.c
main.m