Last week I talked about the difficulty I had casting an id *
pointer across the bridge
to Core Foundation. My solution in that post was to cross the bridge at a different
point. Instead of using my void **
object to create an NSArray, I used it to create a
CFArray, which then easily transfers across the bridge.
Today I ran into a situation where that approach didn’t work. I was trying to implement NSFastEnumeration in SBPriorityQueue. NSFastEnumeration is implemented in a single method call:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
objects:(id __unsafe_unretained *)stackbuf
count:(NSUInteger)len
Since I have a C array holding my objects, the easiest and fastest thing to do is to store
a pointer to that array in the state structure and return the count of the objects in the
array. The enumeration will be truly fast–as fast as a C for
loop. The problem I ran
into is that the field state->itemsPtr is of type __unsafe_unretained id *
. I couldn’t
figure out how to store my pointer of type __strong id *
in that field.
First, the setup. My array of objects is declared and created like this:
__strong id *_heap; //ivar in @implementation
_heap = (__strong id *)calloc(MIN_SIZE + 1, sizeof(id)); //in initializer
I wanted to store my _heap pointer some way similar to this:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
objects:(id __unsafe_unretained *)stackbuf
count:(NSUInteger)len
{
if (state->state == 0) {
NSUInteger count;
state->mutationsPtr = &contentSize;
state->itemsPtr = (__SOME_ARC_CAST)&_heap[1]; // ignoring first array element;
state->state = 1;
return contentSize;
}
return 0;
}
The $64,000 question is: What is the actual cast represented by __SOME_ARC_CAST? First I’ll go over all the options that seemed reasonable, but didn’t work.
state->itemsPtr = (__bridge __unsafe_unretained id *)&_heap[1];
//ERROR: Incompatible types casting '__strong id *' to '__unsafe_unretained id *' with a __bridge cast.
I could reduce the problem from an error to a warning with some questionable use of
const
:
const id *heapPtr = &_heap[1]; // ignoring first array element;
state->itemsPtr = heapPtr;
//WARNING: Assigning to '__unsafe_unretained id *'from 'const id *' discards qualifiers.
I don’t like compiler warnings, so I did some research on the Apple Developer Forum. I read a suggestion to use a double cast, so I tried:
__unsafe_unretained id *heapPtr = (__bridge __unsafe_unretained id *)(void **)&_heap[1];
//ERROR: cast of an indirect pointer to an Objective-C pointer to 'void **' is disallowed with ARC
Closer (though I didn’t know it at the time), but it still produces a compiler error.
Finally, thanks to an Apple engineer, I learned that the secret door out of this maze of
qualifiers is to cast through void *
, not void **
, like so:
state->itemsPtr = (__bridge __unsafe_unretained id *)(void *)&_heap[1];
And with that, fast enumeration is implemented.
I discover and fix the issue causing the slowdown described in the last post.
After updating my priority queue to use GCD, the timing tests indicate it actually slowed down slightly.
Automatic Reference Counting is straightforward when casting from Core Foundation types to Foundation types, but dealing with C arrays can confuse things.
I compare two object-oriented priority queues for Objective-C objects. One using using Core Foundation, another using a C array.