What is core foundation?

A little bit of history

To understand what core foundation is, let us get back in history a little bit. It was the year 2000, and there were two ways to develop applications, for macOS 10.

On one hand, you had carbon, which you could use, for both macos 10, and macos 9 and even for macOS 8, to develop applications. Carbon was a C api.

On the other hand, you had cocoa, which was an objective C api, and which could only be used,for developing applications, for macOS 10.

Core foundation, is a C api, and at that point of time, it acted as a bridge, between these two api, so for example, carbon had Pascal Strings, whereas cocoa had NSString , and core foundation provided CFString, which could act, as a bridge between the two.

The core foundation framework is not needed, to develop Carbon or Cocoa applications , and later on in history, carbon was removed as an option, to develop applications for macOS.

CFTypeRef

At the core, of core foundation, is CFTypeRef, which is defined, as being a void pointer:

typedef 
    const void *
        CFTypeRef;

Being a void pointer, this means that CFTypeRef, can be understood, as having the ability, to point to any data , as such it can be used to pass data around, for example in function calls, or in return arguments.

And another way of thinking about CFTypeRef, but which is not really related to CFTypeRef, is why not, all the data defined in core foundation, having something in common, such as a data structure, containing some information about the data type, such as the type id, and pointers to some functions, that must be defined, for all core foundation types, such as equality, or hashing.

Introspection

This being said, all data types in core foundation, as in CFArray, or CFString, and which act as a prototype, to the actual data, define a type id, which is of type CFTypeID , and which is an integral value, and can be thought as being defined:

typedef 
    unsigned long 
        CFTypeID;

To get the id of an instance of a core foundation data type, where an instance means just allocating memory for this data type, you can use the CFGetTypeID function, defined as follows:

CFTypeID 
    CFGetTypeID 
        (CFTypeRef cf );

Additionally for each defined data type, there is a function, which name has the format ....GetTypeID, where the ... are substituted by the data type name, so for example CFArray would have as the function, which would return its type , CFArrayGetTypeID .

As an example :

#include <CoreFoundation/CoreFoundation.h>

int
        main
            (int argc, const char * argv[ ] ){
    CFStringRef str = CFSTR ("HEllo world" );
    CFTypeID str_id = CFGetTypeID (str );
    printf ("Type id of CFString is : %lu\n", str_id );
    //Type id of CFString is : 7
    str_id = CFStringGetTypeID ( );
    printf ("Type id of CFString is : %lu\n", str_id );
    // Type id of CFString is : 7
    return 0; }

Having gotten the type id, the description of this id, which is typically its name, meaning the name, of the structure, or class of this type, can be gotten , using the CFCopyTypeIDDescription function, as in :

/* 
Function signature of CFCopyTypeIDDescription :
    CFStringRef CFCopyTypeIDDescription (CFTypeID type ); .*/ 

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
    CFStringRef str = CFSTR ("Howdy !?" );
    CFTypeID str_id = CFGetTypeID (str );
    CFStringRef str_id_description = CFCopyTypeIDDescription (str_id ); 
    CFShow (str_id_description );
    //CFString
    CFRelease (str_id_description );
    return 0; }

Getting additional information, about the actual object, can be done, using the function CFCopyDescription. The kind of information gotten by this function, can be useful for debugging purposes.

/* 
Function signature of CFCopyDescription :
    CFStringRef CFCopyDescription (CFTypeRef cf ) .*/

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
    CFStringRef str = CFSTR ("Hola :>" );
    CFStringRef str_description = CFCopyDescription (str ); 
    CFShowStr (str_description );
    /*
     Length 61
     IsEightBit 1
     HasLengthByte 1
     HasNullByte 1
     InlineContents 1
     Allocator SystemDefault
     Mutable 0
     Contents 0x1001085a0 */
    CFRelease (str_description );
    return 0; }

Note that in the previous two examples, the functions CFShow and CFShowStr, were used:

void CFShow (CFTypeRef obj );
void CFShowStr (CFStringRef str );

CFShow will print the debug description, for all core foundation objects, unless this object is a CFString, in which case, it will print the actual string content, whereas CFShowStr, is geared towards strings, and prints a string debug information.

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
    CFStringRef str = CFSTR ("salut" );
    CFShow (str);
    //salut
    CFShowStr (str );
    /*
     Length 5
     IsEightBit 1
     HasLengthByte 0
     HasNullByte 1
     InlineContents 0
     Allocator SystemDefault
     Mutable 0
     Contents 0x100000f5c */
    CFShow (kCFBooleanTrue );
    // <CFBoolean 0x7fff70035280 [0x7fff70033ee0]>{value = true}
    return 0; }

Equality, and hashing

As being explained so far, we want to have certain abilities, over all the core foundation, data types, and this can be thought of, as if all data types, having some common structure, enabling these abilities.

Beside the introspection ability, which was described in the earlier section, all core foundation objects, can be compared for equality, and a hash, can be calculated of.

Two core foundation types, are considered to be equal, if they are of the same type, disregarding mutability, where mutability is can an object be written to, or not.

Additionally, equality is what a type deems equal, so for two CFString types, they are only equal, if their characters are the same, disregarding the encoding, and for arrays, they are considered to be equal, if they have the same length, and their elements are equal.

Equality can be checked by using the function CFEqual .

/* 
CFEqual method signature:
    Boolean CFEqual (CFTypeRef cf1, CFTypeRef cf2 ); */


#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                
    int i = 1;
    CFNumberRef number_a = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &i );
    
    double d = 1.0;
    CFNumberRef number_b = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &d );
    
    if (!CFEqual (number_a, number_b ))
        // Comparing two numbers for equality .
        printf ("%f != %i \n" , d , i );
    // 1.000000 != 1
    
    
    unsigned char utf8_buf[ ] = { 0x68, 0x69 }; 
    // utf-8 , encoding of hi
    CFStringRef string_a = CFStringCreateWithBytes (
                                    kCFAllocatorDefault,
                                    utf8_buf, 
                                    2, 
                                    kCFStringEncodingUTF8, 
                                    FALSE );
    
    
    unsigned char utf16_buf[ ] = { 0x68, 0, 0x69, 0 }; 
    // utf-16 little endian machine , encoding of hi
    CFStringRef string_b = CFStringCreateWithBytes (
                                    kCFAllocatorDefault,
                                    utf16_buf, 
                                    4, 
                                    kCFStringEncodingUTF16, 
                                    FALSE );
    if ( CFEqual (string_a, string_b ))
        // comparing two strings for equality .
        printf ("string_a is equal to string_b \n" );
    // string_a is equal to string_b
    
    
    CFArrayRef array_a = CFArrayCreate (
                                    kCFAllocatorDefault, 
                                    (const void ** ) &string_a, 
                                    1, 
                                    &kCFTypeArrayCallBacks );
    
    CFArrayRef array_b = CFArrayCreate (
                                    kCFAllocatorDefault, 
                                    (const void ** ) &string_b, 
                                    1, 
                                    &kCFTypeArrayCallBacks );
    
    if ( CFEqual (array_a, array_b ))
        // Comparing two arrays for equality .
        printf ("array_a is equal to array_b \n" );
    // array_a is equal to array_b

    CFRelease (number_a );
    CFRelease (number_b );
    CFRelease (string_a );
    CFRelease (string_b );
    CFRelease (array_a );
    CFRelease (array_b );
    return 0; }

In core foundation, two objects, if are equal, then they must have the same hash, the hash of any core foundation object, can be gotten, by using the CFHash function .

/* 
CFHash function signature is:
       CFHashCode CFHash(CFTypeRef cf);

CFHashCode  can be defined as:
    typedef unsigned long CFHashCode; */
#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                
    CFStringRef str = CFSTR ("salut" );
    CFHashCode str_hash = CFHash (str );
    printf ("salut hash is: %lu\n", str_hash );
    // salut hash is: 19472616406254
    return 0; }

Creating core foundation instances of a type

The question to ask at this stage, is how to create an instance of any core foundation type ?

As we have seen in previous examples, for a core foundation string, CFString,the macro CFSTR, was used to get a reference, to a CFString object. But the question is how is it done in general, for all core foundation types.

Basically, to create an instance, of a type in core foundation, you use a function, which contains Create in its name, as in CFStringCreateMutable or CFArrayCreate.

The function receives as it first parameter, a CFAllocator object. An allocator object, can be thought of, as a structure, containing references to some functions, which can be used to allocate, reallocate and free memory.

To give an example of some functions, which can be used to create core foundation objects:

CFDateRef 
    CFDateCreate (
       CFAllocatorRef allocator,
       CFAbsoluteTime at );

CFNumberRef 
    CFNumberCreate (
       CFAllocatorRef allocator,
       CFNumberType theType,
       const void * valuePtr );

CFArrayRef 
    CFArrayCreate (
       CFAllocatorRef allocator,
       void ** values,
       CFIndex numValues,
       const CFArrayCallBacks * callBacks );

CFStringRef 
    CFStringCreateWithCString (
       CFAllocatorRef alloc,
       const char * cStr,
       CFStringEncoding encoding );

There are some predefined allocators that you can use, for example, if the constant kCFAllocatorSystemDefault is specified, this means, that when creating an object,the system allocator is to be used, if NULL or kCFAllocatorDefault is used, this means, use whatever default allocator, which is set.

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                
    CFDateRef date = CFDateCreate (NULL, 0 );
    CFShow (date );
    // 2000-12-31 16:00:00 -0800
    CFRelease (date );
    
    int i = 1;            
    CFNumberRef num = CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &i );
    CFShow (num );
    // <CFNumber 0x10010a2e0 [0x7fff70033ee0]>{value = +1, type = kCFNumberSInt32Type}
    CFRelease (num );
                
    const char * cstr = "hello world";
    CFStringRef str = CFStringCreateWithCString (kCFAllocatorSystemDefault, cstr, kCFStringEncodingASCII );
    CFShowStr (str );
    /* Length 11
     IsEightBit 1
     HasLengthByte 1
     HasNullByte 1
     InlineContents 1
     Allocator SystemDefault
     Mutable 0
     Contents 0x1004005c0 */
    CFRelease (str );
    return 0; }

As you see, that when creating an object, what the creator function actually returns, is a reference, so in the previous example, what you actually got was, CFDateRef , CFNumberRef , and CFStringRef , which are references to CFDate , CFNumber and CFString structures.

De-allocating memory

Having allocated, or created an object, you might ask, how should it be de-allocated, so in other words, how to free its memory.

For core foundation objects instances, you never free memory directly, as in calling free for example, as in c, but instead, a mechanism which is called reference counting, is used to tell the allocator object, which was used to create an instance of a type, to free the instance memory .

Whenever a core foundation function, which has create, or copy, in its name, is used, the number of references, which are said to be owning the object, is set to 1.

An object can have as much references, as you want, like by assigning its address, or its pointer, to another pointer, but it is only when the number of owning references, reaches 0, that an object memory is de-allocated.

Incrementing an object count, of owning references is done by using the CFRetain function, and decrementing an object count, of owning references, is done through the CFRelease function.

CFTypeRef 
    CFRetain 
        (CFTypeRef cf );
/* Increment an object reference count,
returning a reference to the object, 
once done .*/


void 
    CFRelease
        (CFTypeRef cf );
/* Decrement an object reference count .*/

You are responsible, for decrementing the owning references count, for core foundation objects, which you have gotten, using create, copy , or on which reference, you have issued a CFRetain function call, which in other words, incremented the owning reference count.

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                
    CFDateRef date = CFDateCreate(NULL, 0 );
    CFShow (date );
    // 2000-12-31 16:00:00 -0800
    CFDateRef date_non_owning_ref = date;
    CFRelease (date );
    
    /*
     CFShow (date_non_owning_ref );
     This call will cause an error, since
     date is de-allocated, which means
     date_shallow_ref , does not point
     to a CFDate data .*/

                
    const char * cstr = "hello world";
    CFStringRef str = CFStringCreateWithCString (
                           kCFAllocatorSystemDefault, 
                           cstr, 
                           kCFStringEncodingASCII );
    CFStringRef str_owning_ref = CFRetain (str );
    CFRelease (str );
    CFShowStr (str_owning_ref );
    /* 
     Length 11
     IsEightBit 1
     HasLengthByte 1
     HasNullByte 1
     InlineContents 1
     Allocator SystemDefault
     Mutable 0
     Contents 0x10040a230 .*/
    CFRelease (str_owning_ref ); 
    return 0; }

Once a core foundation object, owning reference count, reaches 0, the allocator object, which was used to allocate memory, for the created core foundation instance, is asked to free the memory .

You can get the count of owning references, for any core foundation object, by using the function CFGetRetainCount .

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                           
    const char * cstr = "hello world";
    CFStringRef str = CFStringCreateWithCString (
                          kCFAllocatorSystemDefault, 
                          cstr, 
                          kCFStringEncodingASCII );
    printf ("retain count is : %ld\n", CFGetRetainCount (str ));
    // retain count is : 1
    CFStringRef str_owning_ref = CFRetain (str );
    printf ("retain count is : %ld\n", CFGetRetainCount (str ));
    // retain count is : 2
    CFRelease (str );
    printf ("retain count is : %ld\n", CFGetRetainCount (str ));
    // retain count is : 1
    CFRelease (str_owning_ref ); 
    return 0; }

Core foundation, also has a function named, CFAutorelease . What this function does, is that it will add a core foundation object, which you have allocated, to an auto release pool, meaning, that once the end block, of the auto-release pool is reached, a CFRelease call, is made onto that object. So in other words, it is a way for a core foundation object, to auto decrement, its owning reference count, at the end of an auto release block.

#import <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>

CFNumberRef one_ref ( ){
    int x = 1;
    CFNumberRef cf_number_ret = CFNumberCreate (
                                            kCFAllocatorDefault,
                                            kCFNumberIntType, 
                                            &x );
    return CFAutorelease (cf_number_ret ); }


int main (int argc, const char * argv [ ] ){
    @autoreleasepool {
        CFNumberRef cf_number = one_ref ( ); }
    return 0; }

For other functions in core foundation, that return a non owning reference, such as functions containing get in the name, you can use CFRetain, to make the returned non owning reference, into an owning one.

#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv[ ] ){
   
    CFStringRef  values [ ] = {CFSTR ("hello" ) , CFSTR ("hi" )};
    
    CFArrayRef cf_array = CFArrayCreate (
                                kCFAllocatorDefault, 
                                (const void ** ) values, 
                                2, 
                                &kCFTypeArrayCallBacks );
    
    CFStringRef str_a = CFArrayGetValueAtIndex
                            (cf_array, 
                             1 );
    /* At this stage, it is not necessary to retain
     str_a, since we know that the array, is not
     going to be released, but this can be done
     nevertheless .*/
    CFRetain (str_a );
    CFShow(str_a);
    // hi
    
    CFRelease (str_a );
    CFRelease (cf_array );  

    return 0; }

Other framework concepts

Opaque and non Opaque

Some core foundation types, as in CFNumber, are opaque, so in other words, you do not interact with these structures directly, but instead you use references as in CFStringRef, which you pass around to functions, in order to perform the desired operation, on the data.

#include <CoreFoundation/CoreFoundation.h>

int 
        main 
            (int argc, const char * argv[ ] ){
                
    double d = 1.2;
    CFNumberRef cf_number = CFNumberCreate (NULL, kCFNumberDoubleType, &d );
    
    
    if (CFNumberIsFloatType(cf_number ))
        printf ("cf_number is a float type\n" );
    // cf_number is a float type
    
    
    int i;
    CFNumberGetValue (cf_number, kCFNumberIntType, &i );
    CFNumberGetValue (cf_number, kCFNumberIntType, &d );
    printf ("%f integer value is : %d\n",d,i );
    // 1.200000 integer value is : 1
    
    CFRelease (cf_number );
    return 0; }

Other core foundation types , such as CFRange, are not opaque, so you know exactly what they represent, and you can interact with their values directly.

 CFRange range ;
/* CFRange is a range, so it
has a start location, and a 
span, or a length .
CFrange is defined as :
typedef struct {
    CFIndex location;
    CFIndex length;
} CFRange; */
range .location = 1;
range .length = 2;

/* CFIndex represents indexes,
counts, length, and sizes, and 
is used in core foundation, for
certain functions parameters, 
and return value. 
CFIndex is defined as :
  typedef long CFIndex; */
CFIndex count = 1l;

Naming conventions

Core foundation data types, CF'Type', start with CF followed by the type name, as in CFNumber or CFDictionary

A type has a set of actions, CF'TypeAction', that can be performed onto that type. Some actions can be thought of, as being more or less, common to all the types, such as creating, copying, comparing, or getting a value, out of a type. Other actions, are type specific, such as checking, if a number is a float. In all cases, naming of actions is consistent.

/* Creating the data, will
set the retain count to 1,
hence data must be released. 
Functions that create data, have
Create in their name .*/
CFNumberCreate
CFStringCreateWithBytes 
CFStringCreateWithFormat
CFRunLoopTimerCreate


/* Copying the data will 
set the retain count  to 1,
hence data must be released .*/
CFStringCreateCopy
CFDictionaryCreateCopy


/* Getting values out of data .*/
CFStringGetCString
CFStringGetLength
CFNumberGetValue
CFRunLoopTimerGetInterval


/* Comparison functions .*/
CFStringCompare
CFNumberCompare
...


/* Type specific functions .*/
CFStringFind
CFStringHasPrefix
CFNumberIsFloatType

A type also has, a set of constants, which are associated with it,
KCF'Type''Constant' , the constants related to a type, start with a K, followed by the type name, followed by the constant name , for example kCFNumberShortType.

Core foundation constants, can have multiple usages, and they can be of different types.

enum CFNumberType {
/* indicates the type of
a number, for example, 
used with a core foundation
function, which creates a 
core foundation number, out of
a primitive integer, or floating
value .*/
...
   kCFNumberCharType = 7,
   kCFNumberShortType = 8,
   kCFNumberIntType = 9,
   kCFNumberLongType = 10,
   kCFNumberLongLongType = 11,
   kCFNumberFloatType = 12,
   kCFNumberDoubleType = 13,
...};


enum CFStringCompareFlags {
/* Indicates for example,
for a core foundation string
comparing function, the mode 
under which, string comparison is 
to be performed .*/
   kCFCompareCaseInsensitive = 1,
   kCFCompareBackwards = 4,
   kCFCompareAnchored = 8,
   kCFCompareNonliteral = 16,
   kCFCompareLocalized = 32,
   kCFCompareNumerically = 64 };


#define kCFStringEncodingInvalidId (0xffffffffU)
/* Returned by a function, 
to indicate that a string 
encoding, is not supported by 
CFString .*/


const CFNumberRef kCFNumberNaN;
/* kCFNumberNaN is an instance of
CFNumber , so it is a predefined
value, which indicates Not a number .*/


const CFArrayCallBacks kCFTypeArrayCallBacks;
/* Collections, when being created 
must be passed a non opaque structure, 
which defines certain call back functions.
Check the Collections callback section,
for more details .*/

Some data types, have other data types, related to them , for example CFString, has CFStringRef .

Once the core foundation naming conventions are understood, it should become more easier, to work with the core foundation framework, and you will have a grasp, about how things are connected, in this framework.

Mutability

Certain data types in core foundation, can be created as being immutable, hence they would have a fixed size and content, or they could be created, in between, as having the content mutable, and the size fixed, or they could be created, as fully mutable, which means that both the size and the content, are mutable.

An example of such kinds, are : CFString , CFData, CFArray , and CFDictionary .

#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv[ ] ){
   
    CFStringRef dict_keys [ ] = {CFSTR ("key_1" ) , CFSTR ("key_2" )};
    CFStringRef dict_values [ ] = {CFSTR ("value_1" ) , CFSTR ("value_2" )};
    
    /* Create an immutable dictionary */
    CFDictionaryRef cf_dict_a = CFDictionaryCreate (
                                    kCFAllocatorDefault, 
                                    (const void ** ) dict_keys, 
                                    (const void ** ) dict_values,
                                    2,
                                    &kCFCopyStringDictionaryKeyCallBacks, 
                                    &kCFTypeDictionaryValueCallBacks );
    CFShow (cf_dict_a );
    /*<CFBasicHash 0x100106330 [0x7fff70033ee0]>{type = immutable dict, count = 2,
     entries =>
     0 : <CFString 0x100001098 [0x7fff70033ee0]>{contents = "key_1"} = <CFString 0x1000010d8 [0x7fff70033ee0]>{contents = "value_1"}
     1 : <CFString 0x1000010b8 [0x7fff70033ee0]>{contents = "key_2"} = <CFString 0x1000010f8 [0x7fff70033ee0]>{contents = "value_2"}
     }*/
    
    
    /* Create a dictionary, with mutable contents, and
       a fixed size, by specifying the capacity .*/
    CFMutableDictionaryRef cf_dict_b = CFDictionaryCreateMutable(
                                    kCFAllocatorDefault, 
                                    2, 
                                    &kCFCopyStringDictionaryKeyCallBacks, 
                                    &kCFTypeDictionaryValueCallBacks) ;
    CFDictionaryAddValue (cf_dict_b, CFSTR ("key_a" ), CFSTR ("value_a" ));
    CFDictionaryAddValue (cf_dict_b, CFSTR ("key_b" ), CFSTR ("value_b" ));
    CFShow (cf_dict_b );
    /*<CFBasicHash 0x100106370 [0x7fff70033ee0]>{type = mutable dict, count = 2,
     entries =>
     0 : <CFString 0x100001118 [0x7fff70033ee0]>{contents = "key_a"} = <CFString 0x100001138 [0x7fff70033ee0]>{contents = "value_a"}
     1 : <CFString 0x100001158 [0x7fff70033ee0]>{contents = "key_b"} = <CFString 0x100001178 [0x7fff70033ee0]>{contents = "value_b"}
     }*/
    
    
    /* Create a dictionary, with dynamic size, and mutable
       content, by setting the capacity to 0 .*/
    CFMutableDictionaryRef cf_dict_c = CFDictionaryCreateMutable(
                                    kCFAllocatorDefault, 
                                    0, 
                                    &kCFCopyStringDictionaryKeyCallBacks, 
                                    &kCFTypeDictionaryValueCallBacks) ;
    CFDictionaryAddValue (cf_dict_c, CFSTR ("a_key" ), CFSTR ("a_value" ));
    CFShow (cf_dict_c );
    /*<CFBasicHash 0x100108620 [0x7fff70033ee0]>{type = mutable dict, count = 1,
     entries =>
     0 : <CFString 0x100001198 [0x7fff70033ee0]>{contents = "a_key"} = <CFString 0x1000011b8 [0x7fff70033ee0]>{contents = "a_value"}
     }*/
    
    CFRelease (cf_dict_a );
    CFRelease (cf_dict_b);
    CFRelease (cf_dict_c );
    return 0; }

Out , and first parameter

Functions others then the create functions, will have as a first parameter, an instance of the type.

CFRange 
    CFStringFind (
        CFStringRef theString,
        CFStringRef stringToFind,
        CFOptionFlags compareOptions );

CFIndex 
    CFNumberGetByteSize (
        CFNumberRef number );

Functions could have what is called, an out parameter, this is just an argument, which is passed by reference to a function, and this function, will set or change its value. An out parameter, is typically the last parameter, in core foundation functions.

void 
    CFStringGetLineBounds (
        CFStringRef theString,
        CFRange range,
        CFIndex * lineBeginIndex,
        CFIndex * lineEndIndex,
        CFIndex * contentsEndIndex );
/* lineBeginIndex , lineEndIndex and 
contentsEndIndex  are out parameters .*/

Collections callback

Certain collection types, as in CFArray, CFSet and CFDictionary, take a pointer, to a callback structure, when being created, for example the signature of the create function, of CFArray , is as follows:

CFArrayRef 
    CFArrayCreate (
        CFAllocatorRef allocator,
        void ** values,
        CFIndex numValues,
        const CFArrayCallBacks * callBacks );

A callback is a non opaque structure, so example of this structure definition, for various collection types, is :

struct CFArrayCallBacks {
   CFIndex version;
   CFArrayRetainCallBack retain;
   CFArrayReleaseCallBack release;
   CFArrayCopyDescriptionCallBack copyDescription;
   CFArrayEqualCallBack equal; };


struct CFDictionaryKeyCallBacks {
   CFIndex version;
   CFDictionaryRetainCallBack retain;
   CFDictionaryReleaseCallBack release;
   CFDictionaryCopyDescriptionCallBack copyDescription;
   CFDictionaryEqualCallBack equal;
   CFDictionaryHashCallBack hash; };

struct CFDictionaryValueCallBacks {
   CFIndex version;
   CFDictionaryRetainCallBack retain;
   CFDictionaryReleaseCallBack release;
   CFDictionaryCopyDescriptionCallBack copyDescription;
   CFDictionaryEqualCallBack equal; };

struct CFSetCallBacks {
   CFIndex version;
   CFSetRetainCallBack retain;
   CFSetReleaseCallBack release;
   CFSetCopyDescriptionCallBack copyDescription;
   CFSetEqualCallBack equal;
   CFSetHashCallBack hash; };

The fields for a callback structure , are :

The version number of the structure, which is typically 0.

A retain function, which is going to be called, when an object is added to the collection, this is like the CFRetain function.

A release function, called when an object is removed from the collection, so this is like the CFRelease function.

A copyDescription function, called for each object, when the CFCopyDescription function, is called on the collection.

An equal function, which is used to compare the objects, of the collection for equality.

Possibly a hash function, for certain collection types, which would compute a hash, as in a key hash for a dictionary.

If the collection, is going to contain, core foundation types, you can just pass in, some predefined callbacks, which would for example, handle incrementing and decrementing core objects reference count, and equality for you. For the CFArray type, you can pass in, the predefined kCFTypeArrayCallBacks constant.

If you want to disable certain functions of being used, you can create your instance of a callback structure, and set the functions you want to be disabled, to NULL.

For example, if you are not passing core foundation objects, and as such, it does not make sense, to ask the collection to automatically increase the retain count of the objects, when being added, and decrease it, when being removed, you can set the retain, and release functions to NULL. Collections contain references to objects.

#include <CoreFoundation/CoreFoundation.h>

Boolean 
        int_equal 
            (const void * value1, const void * value2 ){
    return !strcmp((char * ) value1, (char * ) value2 ); }

CFStringRef 
        int_description 
            (const void * value ){
    char * str =  (char * ) value ;
    return CFStringCreateWithCString (kCFAllocatorDefault, str, kCFStringEncodingUTF8 ); }


int main (int argc, const char * argv[ ] ){
   
    CFArrayCallBacks cf_arrayCallbacks = {0 };
    cf_arrayCallbacks .equal = int_equal;
    cf_arrayCallbacks .copyDescription = int_description;

    
    char * s_array_a[ ] = {"hello", "world" };
    CFArrayRef cf_array_a = CFArrayCreate (
                                    kCFAllocatorDefault, 
                                    (const void ** ) s_array_a, 
                                    2, 
                                    &cf_arrayCallbacks );
    
    char * s_array_b[ ] = {"hey", "there" };
    CFArrayRef cf_array_b = CFArrayCreate (
                                    kCFAllocatorDefault, 
                                    (const void ** ) s_array_b, 
                                    2, 
                                    &cf_arrayCallbacks );

    CFShow (cf_array_a );
    /*<CFArray 0x1001061b0 [0x7fff70033ee0]>{type = immutable, count = 2, values = (
     0 : hello
     1 : world
     )}*/
    
     CFShow (cf_array_b );
    /*<CFArray 0x100106330 [0x7fff70033ee0]>{type = immutable, count = 2, values = (
     0 : hey
     1 : there
     )}*/
    
    if (!CFEqual (cf_array_a, cf_array_b ))
        printf ("cf_array_a is not equal to cf_array_b\n" );
    /*cf_array_a is not equal to cf_array_b */
    
    CFRelease (cf_array_a );
    CFRelease (cf_array_b );
    return 0; }

Core foundation services?

is not

Core foundation is not a GUI framework, so it does not provide functions, for drawing, or creating a graphical user interface, these tasks are handled by other frameworks , for example cocoa, which uses foundation, the objective-c counterpart of core foundation, and Appkit.

Data structures

What core foundation does, is that it provides certain data structures, which can be used for storage, such as CFArray, or CFDictionary .

Internationalization

Unicode is capable of representing, all the characters in the world. Core foundation offers CFString, where characters are conceptually in unicode, and encoding is internal.

A CFString, is capable of being converted, or created, from any other encoding. So you can think of CFString, as a way, to provide localization and internationalization .

Bundles

An application resources are stored into a bundle.

Photo.app
    Contents
        Info.plist
        version.plist
        MacOS
            Photo
        PkgInfo
        Resources
            English.lproj
                Menu.nib
                sounds
                images
                InfoPlist.strings
                Localizable.strings
                ...
            French.lproj
                Menu.nib
                sounds
                images
                InfoPlist.strings
                Localizable.strings
                ...
            img_0.png
            Photo.help
            ..

A bundle is more or less, your core application, so it contains your application executables, localization files, property lists, and other resources or things, which are related to your application.

Core foundation provides an opaque data structure, CFBundle, to represent a bundle, and to work with its various aspects, like for example, getting a reference to your application bundle, and using that reference, to get the location, of a file in a bundle ..

CFBundleRef main_bundle = CFBundleGetMainBundle ( );
CFRetain (main_bundle );
/* Get the application main bundle .*/

CFArrayRef  jpegs_french = CFBundleCopyResourceURLsOfTypeForLocalization (
     main_bundle, 
     CFSTR ("jpg" ),  //type
     CFSTR ("images" ), //folder
     CFSTR ("French" )); //locale
/* Multiple functions exist, to get a URL,
for files in your bundle .*/
CFRelease (jpegs_french );


CFURLRef  image_0 = CFBundleCopyResourceURL ( 
                       mainBundle, //bundle
                       CFSTR ("img_0" ), //name
                       CFSTR ("png"), //type
                       NULL ); // subdirectory

CGDataProviderRef provider = CGDataProviderCreateWithURL (url );
CGImageRef image = CGImageCreateWithPNGDataProvider (
                       provider, // data source
                       NULL, //decode array
                       false, //interpolate
                       kCGRenderingIntentDefault ); //color mapping

/* do something .*/


CGImageRelease (image );
CGDataProviderRelease (provider );
CFRelease (image_0 );
CFRelease (main_bundle );

Property List

As seen in the previous section, a bundle can contain property list files, such as Info.plist, which provides information, about the application, as its name , its id, and other stuff.

On disk, multiple representations exist, for a property list, so let us take the xml storage, to better understand, what a property list is.

If a property list is stored in xml, the xml file, will have exactly one root element, plist, which must contain exactly one child.

The child of a plist, corresponds to a core foundation, or an objective-c type . For example, <string>, would correspond to the core foundation CFString, and to the objective-C, NSString .

<!-- Exemple a plist file-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <array>
        <string>Hey</string>
        <string>Cool</string>
    </array>
</dict>

<!-- Exemple Info.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleIdentifier</key>
    <string>com.site.test</string>
    <key>CFBundleName</key>
    <string>test</string>
    <key>CFBundleExecutable</key>
    <string>test</string>
    ...
</dict>
</plist>

Basically a property list, can contain the following core foundation types : CFString , CFNumber , CFDate, CFBoolean , CFData , CFArray, and CFDictionary, where a dictionary keys, must be of the type CFString .

CFPropertyListRef, can represent a property list, so any of the previously mentioned core foundation types .

#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv[ ] ){
   
    CFStringRef values [ ] = { CFSTR ("A" ), CFSTR ("B" )};
    
    CFArrayRef properties_array = CFArrayCreate (
                            kCFAllocatorDefault, 
                            (const void ** ) values,
                            2, 
                            &kCFTypeArrayCallBacks );
    
    
    CFErrorRef error = NULL;
    CFDataRef data_xml = CFPropertyListCreateData (
                            kCFAllocatorDefault, 
                            properties_array, 
                            kCFPropertyListXMLFormat_v1_0, 
                            0, // should be set to 0
                            &error);
    
    if (error == NULL ){
        /* Successfully created, an xml 
         property list, data object */ 
        CFURLRef save_location = CFURLCreateWithFileSystemPath (
                                kCFAllocatorDefault,
                                CFSTR ("/tmp/myprop.xml"),
                                kCFURLPOSIXPathStyle,
                                false );      
       if (!CFURLWriteDataAndPropertiesToResource(
                                        save_location, 
                                        data_xml, 
                                        NULL,  // properties to write
                                        NULL )) //error code
           printf("Error saving file\n" );
        //write property list to file 
        
        CFPropertyListRef property_list_ref =  CFPropertyListCreateWithData (
                                                    kCFAllocatorDefault, 
                                                    data_xml, 
                                                    kCFPropertyListImmutable, 
                                                    NULL, /* If not null, it will be set to data format .*/
                                                    &error );
        if (error == NULL ){
            CFShow (property_list_ref);
            CFRelease (property_list_ref); }
        /* <CFArray 0x1001089a0 [0x7fff70033ee0]>{type = mutable-small, count = 2, values = (
         0 : <CFString 0x7fff70019430 [0x7fff70033ee0]>{contents = "A"}
         1 : <CFString 0x7fff700146d0 [0x7fff70033ee0]>{contents = "B"}
         )} 
         
         CFURLCreateDataAndPropertiesFromResource can be used
         to create a property list from a file. */

        CFRelease(save_location );
        CFRelease (data_xml ); }
    else
        CFRelease (error );
    
    CFRelease (properties_array );
    return 0; }

Preferences

Additionally core foundation, allows you to set and retrieve preferences. Preferences are also saved as property list, but in this case, you do not interact with the property list, but you just specify a key and a value, that you wish to save . The key is a string , and the value can be CFString , CFNumber , CFDate, CFBoolean , CFData , CFArray, and CFDictionary .

Preferences can be used to stored a user preference, for a specific application or in general, for example, the font a user prefers.

This being said, preferences can be set, for the current user, or for any user, for the current application ,or for any application, for the current host, or any host.

As an example, when using the CFPreferencesSetAppValue function, you are setting the preferences, for the current user, on any host, and for the current application, as such the preference is saved in ~/Library/Preferences/[app_id|app_name] .

app_id is the application id, configured in the application Info.plist , and if the application, does not have an app_id, then the application name is used .

#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv[ ] ){
   
    CFStringRef pref_name = CFSTR ("font_name" );
    CFStringRef pref_value = CFSTR ("Georgia" );
    
    
    CFPreferencesSetAppValue (pref_name, 
                              pref_value,
                              kCFPreferencesCurrentApplication );
    /* Create a preference name and value,
       If null is passed for the value, the 
       preference is deleted. The preference 
       will be saved in :
       ~/Library/Preferences/[app_id|app_name] .*/
    
    
    CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication );
    // Save the preference to disk .
    
    
    
    CFStringRef pref_invalid = CFSTR ("invalid" );
    pref_value = CFPreferencesCopyAppValue (
                        pref_invalid,
                        kCFPreferencesCurrentApplication );
    /* Retrieve a preference, for the 
       current application, as specified 
       in the application id.
       Returns null, if a preference with
       the given name, does not exist .
       Preference can exist in multiple
       domains, the search for a preference
       in this case, starts for the current user , 
       current application , current host , after 
       that current user, current application , 
       any host ... */
    if (pref_value == NULL )
        printf ("Preference invalid is not found\n" );
    //Preference invalid is not found
    
    
    
    pref_value = CFSTR ("Arial" );
    
    CFPreferencesSetValue (pref_name, 
                          pref_value, 
                          kCFPreferencesCurrentApplication,
                          kCFPreferencesAnyUser, 
                          kCFPreferencesCurrentHost );
    /* Create a preference name and value,
       the preference will be saved in 
       /Library/Preferences/[app_id|app_name].
       If null is passed for the value, 
       the preference is deleted .*/

    CFPreferencesSynchronize ( kCFPreferencesCurrentApplication,
                             kCFPreferencesAnyUser, 
                             kCFPreferencesCurrentHost );
    /* Write the preference to disk */
    
    
    pref_value = CFPreferencesCopyValue (pref_invalid, 
                           kCFPreferencesCurrentApplication,
                           kCFPreferencesAnyUser, 
                           kCFPreferencesCurrentHost );
    /* Check if a preference exist. The preference
       is stored for the current application, any
       user, and current host .
       The function returns NULL, if such preference does
       not exist .*/

    if (pref_value == NULL )
        printf ("Preference invalid is not found\n" );
    // Preference invalid is not found
    
    return 0; }

As seen in the example, preferences are searched for, in a given order :

  • current user, current application, current host
  • current user, current application, any host
  • current user, any application, current host
  • current user, any application, any host
  • any user, current application, current host
  • any user, current application, any host
  • any user, any application, current host
  • any user, any application, any host

Toll free bridging

As it was seen earlier, for a property list, its values can be mapped to either a core foundation type, or a foundation type. So for example <array>, in a property list represents both CFArray in core foundation, and NSArray in foundation .

This being said, certain data types, in core foundation, and in foundation, can be thought of, as being the same, which means you can cast between the two data types, and after casting, functions of one, could be used on the other .

Core foundationFoundation
CFNullRefNSNull
CFBooleanRefNSNumber
CFNumberRefNSNumber
CFCharacterSetRefNSCharacterSet
CFStringRefNSString
CFAttributedStringRefNSAttributedString
CFArrayRefNSArray
CFDictionaryRefNSDictionary
CFSetRefNSSet
CFTimeZoneRefNSTimeZone
CFDateRefNSDate
CFCalendarRefNSCalendar
CFLocaleRefNSLocale
CFURLRefNSURL
CFErrorRefNSError
CFReadStreamRefNSInputStream
CFWriteStreamRefNSOutputStream
CFDataRefNSData
CFRunLoopTimerRefNSTimer
CFMutableCharacterSetRefNSMutableCharacterSet
CFMutableStringRefNSMutableString
CFMutableAttributedStringRefNSMutableAttributedString
CFMutableArrayRefNSMutableArray
CFMutableDictionaryRefNSMutableDictionary
CFMutableSetRefNSMutableSet
CFMutableDataRefNSMutableData

When foundation had manual reference counting, casting between toll free bridge foundation and core foundation types, was straightforward, since both framework, used the same percepts, of retain and release, to decide when memory, was going to be freed.

With foundation moving to automatic reference counting , when casting was to be performed, for these toll free bridge types, you had to specify, who was going to control memory management, so who is responsible for freeing memory .

#import <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv [ ] )
{
    @autoreleasepool {
        NSNumber* ns_number_1 = [[NSNumber alloc ] initWithInt: 1 ];
        CFNumberRef cf_number_1 = (__bridge CFNumberRef ) ns_number_1;
        /* When the compiler directive __bridge 
           is used, memory management is not 
           transferred in either way. Hence
           it is not necessary to release 
           cf_number, since ns_number memory is 
           subject to ARC , which is automatic 
           reference counting.
           Before arc, you could just do
           CFNumberRef cf_number_1 = (CFNumberRef ) ns_number_1;
           since both foundation, and core
           foundation, used manual reference
           counting, MRC  .*/
        CFShow (cf_number_1 );
        /* <CFNumber 0x100108d90 [0x7fff70033ee0]>{value = +1, type = kCFNumberSInt32Type} */
        
        
        NSNumber* ns_number_2 = [[NSNumber alloc ] initWithInt: 2 ];
        CFNumberRef cf_number_2 = (__bridge_retained CFNumberRef) ns_number_2;  
        /* When using __bridge_retained, when 
           casting from foundation to core 
           foundation, it means that core 
           foundation, has retained memory management,
           so now it is up, to core foundation,
           to free memory .*/
        CFShow (cf_number_2 );
        CFRelease (cf_number_2);
        /* <CFNumber 0x10010c7c0 [0x7fff70033ee0]>{value = +2, type = kCFNumberSInt32Type} .*/
        
        
        int i = 3;
        CFNumberRef cf_number_3 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i );
        NSNumber* ns_number_3 = (__bridge_transfer NSNumber* ) cf_number_3;
        CFShow (cf_number_3 );
        /*kCFNumberSInt32Type}
         <CFNumber 0x10010c7e0 [0x7fff70033ee0]>{value = +3, type = kCFNumberSInt32Type} */
        NSLog(@"%@", ns_number_3 );
        /* .. test[4517:903] 3 .*/
        
        /* When using __bridge_transfer, to
           cast from core foundation, to foundation,
           it means transfer memory management, from 
           core foundation to foundation ARC, which 
           is automatic memory management, hence it 
           is not necessary to release cf_number_3 .*/}
    return 0; }