Is it safe to put NSObject pointers into a plain C array?

I want to implement a stack that works quite like a circular buffer.

void** pp = malloc(sizeof(id) * 100);

pp[index] = someObject;
// later on somewhere:
MyObject* myObj = (MyObject*)pp[index];

Is this correct? Does this approach work with ARC?

Answered by DTS Engineer in 800070022

No, it's not safe as you have written it.

Since you've told the compiler that pp contains only raw pointers (void**), the stored object reference doesn't have a corresponding retain count. This means that the pp array isn't keeping its objects alive, and they're subject to deallocation if there happen to be no other references.

You can get this one step better by declaring pp as MyObject** instead of void**. In that case, storing an object pointer in the array will store a strong reference. IOW, the object's retain count will be incremented when you store a reference in the array, keeping the object alive even if all other references disappear.

However, it's still not that simple:

  1. When you store a reference in the array (including the first time after you allocated the array), the previous reference at that index will get released. Initially, there are no guarantees about the contents of the malloc'ed block, so you would need to explicitly set all of its bytes to 0, essentially initializing everything in the array to nil. Or, use calloc instead of malloc.

  2. When you lose or free your pointer to the malloc'ed array, you would need to make sure that you caused every reference in the array to decrement its retain count, since you're also discarding all the references in the array. You could do this by setting every element of the array (when typed MyObject**) to nil.

Now, there's already API that handles all of these details for you: it's NSArray. Use NSArray for a simple, reliable solution.

No, it's not safe as you have written it.

Since you've told the compiler that pp contains only raw pointers (void**), the stored object reference doesn't have a corresponding retain count. This means that the pp array isn't keeping its objects alive, and they're subject to deallocation if there happen to be no other references.

You can get this one step better by declaring pp as MyObject** instead of void**. In that case, storing an object pointer in the array will store a strong reference. IOW, the object's retain count will be incremented when you store a reference in the array, keeping the object alive even if all other references disappear.

However, it's still not that simple:

  1. When you store a reference in the array (including the first time after you allocated the array), the previous reference at that index will get released. Initially, there are no guarantees about the contents of the malloc'ed block, so you would need to explicitly set all of its bytes to 0, essentially initializing everything in the array to nil. Or, use calloc instead of malloc.

  2. When you lose or free your pointer to the malloc'ed array, you would need to make sure that you caused every reference in the array to decrement its retain count, since you're also discarding all the references in the array. You could do this by setting every element of the array (when typed MyObject**) to nil.

Now, there's already API that handles all of these details for you: it's NSArray. Use NSArray for a simple, reliable solution.

Is it safe to put NSObject pointers into a plain C array?
 
 
Q