Casting Indirect Pointers With ARC

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.