Table of Contents
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 foundation | Foundation |
---|---|
CFNullRef | NSNull |
CFBooleanRef | NSNumber |
CFNumberRef | NSNumber |
CFCharacterSetRef | NSCharacterSet |
CFStringRef | NSString |
CFAttributedStringRef | NSAttributedString |
CFArrayRef | NSArray |
CFDictionaryRef | NSDictionary |
CFSetRef | NSSet |
CFTimeZoneRef | NSTimeZone |
CFDateRef | NSDate |
CFCalendarRef | NSCalendar |
CFLocaleRef | NSLocale |
CFURLRef | NSURL |
CFErrorRef | NSError |
CFReadStreamRef | NSInputStream |
CFWriteStreamRef | NSOutputStream |
CFDataRef | NSData |
CFRunLoopTimerRef | NSTimer |
CFMutableCharacterSetRef | NSMutableCharacterSet |
CFMutableStringRef | NSMutableString |
CFMutableAttributedStringRef | NSMutableAttributedString |
CFMutableArrayRef | NSMutableArray |
CFMutableDictionaryRef | NSMutableDictionary |
CFMutableSetRef | NSMutableSet |
CFMutableDataRef | NSMutableData |
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; }