We create a new mutable array object using the function CFArrayCreateMutable which has following signature:
CFMutableArrayRef CFArrayCreateMutable( CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks* callBacks);
Where the first parameter gives the type of allocator used to allocate memory for this array, second gives its capacity and third parameter is a pointer to CFArrayCallBacks structure which has following declaration:
typedef struct { CFIndex version; CFArrayRetainCallBack retain; CFArrayReleaseCallBack release; CFArrayCopyDescriptionCallBack copyDescription; CFArrayEqualCallBack equal; } CFArrayCallBacks;
Typically, to create an array in which we intend to put only CFType object references like CFPluginRef, CFStringRef etc, we write code something like this:
CFMutableArrayRef _stringContainer = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
and then add objects to this array at a later time as:
CFStringRef firstString = CFSTR("FirstStringObject"); CFStringRef secondString = CFSTR("SecondStringObject"); CFArrayAppendValue( _stringContainer, firstString ); CFArrayAppendValue( _stringContainer, secondString );
However, we cannot write code like this:
class MyClass // This is a C++ class. { //Declarations }; MyClass* myClassRef = new MyClass; CFMutableArrayRef _myClassContainer = CFArrayCreateMutable( kCFAllocatorDefault, 0, & kCFTypeArrayCallBacks ); CFArrayAppendValue( _stringContainer, myClassRef );
Although, the compiler will not complain but the memory management issues can come up, if we rely on default behaviour of CFArrayRef functions for C++ object references. As the matter of fact, the CFArrayRef uses the CFArrayCallBacks structure reference and calls the functions defined by the function pointers(in the form of CallBacks) for different purposes. For example, when we add a new object to the array it calls the function defined by CFArrayRetainCallBack function pointer. Similarly, if we release the entire array, it calls the function defined by CFArrayReleaseCallBack member and so on.
The default kCFTypeArrayCallBacks which we have used at the time of array creation is a specific CFArrayCallBacks type which works for CFType objects like CFStringRef, CFPluginRef, CFBundleRef etc.
The correct way to make CFMutableArray work well with C++ object pointers, we first need to define a callback function that has same signature as:
typedef void (*CFArrayReleaseCallBack)(CFAllocatorRef allocator, const void *value);
An example function could be as follows:
void ArrayReleaseCallBack(CFAllocatorRef allocator, const void* value) { if (value != NULL) { MyClass* component = reinterpret_cast < MyClass*>(const_cast(value)); delete component; } }
After this define a CFArrayCallBacks structure passing the name of above defined function as third parameter of the type CFArrayReleaseCallBack. Then, pass the reference of this structure to CFArrayCreateMutable’s third parameter as shown:
MyClass* myClassRef = new MyClass; const CFArrayCallBacks arrayCallBack = {0, 0, ArrayReleaseCallBack, 0, 0}; CFMutableArrayRef _myClassContainer = CFArrayCreateMutable(kCFAllocatorDefault, 0, &arrayCallBack ); CFArrayAppendValue( _stringContainer, myClassRef );
This causes the array _myClassContainer to call ArrayReleaseCallBack function of arrayCallBack structure whenever it sends release message to objects contained in it. The appropriate implementation of ArrayReleaseCallBack function would then delete the object for which this callback is being called.
One point to note here is that, since we have not used any callback for retaining the object when it is put in the array(the second member of arrayCallBack structure) we must not call delete on MyClass pointer after putting the object in the array as this would call extra delete call to myClassRef pointer and for sure cause problems at run-time