All is fine and well with ARC in Objective-C land. However, as soon as you also work with C or C++ code — such as the Chipmunk or Box2D libraries, or the Core Foundation framework — you may find yourself facing compiler errors.
Fortunately, it's half as bad if you understand why the compiler keeps bugging you.
Errors you'll see when casting id to void* and vice versa
Most errors when working with C/C++ code has to do with the fact that you can't freely cast id to void* and vice versa. For example, the below example won't work:
NSObject* myObject = [[NSObject alloc] init];
void * myObjectPointer = myObject;
void * myObjectPointer2 = NULL;
NSObject* myObject2 = myObjectPointer2;
|
You'll get an error like this one:
error: cannot initialize a variable of type '...' with an lvalue of type '...'
The types in the error message mentioned may differ but you'll see a __strong keyword thrown in for good measure. Well, you might think this can be fixed by properly casting to the correct types:
NSObject* myObject = [[NSObject alloc] init];
void * myObjectPointer = ( void *)myObject;
void * myObjectPointer2 = NULL;
NSObject* myObject2 = (NSObject*)myObjectPointer2;
|
This time the error is different, each of the two casts above generates a different message:
1: Cast of Objective-C pointer type 'NSObject *' to C pointer type 'void *' requires a bridged cast
2: Cast of C pointer type 'void *' to Objective-C pointer type 'NSObject *' requires a bridged cast
What is a "bridged cast"?
ARC adds the keywords __bridge, __bridge_transfer and __bridge_retained which must be used when casting. Here's the above example with a "bridged cast" that fixes the compile errors:
NSObject* myObject = [[NSObject alloc] init];
void * myObjectPointer = (__bridge void *)myObject;
void * myObjectPointer2 = NULL;
NSObject* myObject2 = (__bridge NSObject*)myObjectPointer2;
|
The use of __bridge in the cast tells the compiler that the object's lifetime doesn't change, and that you understand that if the object's memory is released it may result in a dangling pointer and a potential crash. This is the same behavior to non-ARC code, but ARC really wants to make sure you understand the implications and potential dangers. In ARC terms, you specify that the cast creates a "unsafe unretained" reference.
Bridge casts for Core Foundation
When interfacing with Core Foundation you may want to use __bridge_retain to tell ARC to take on ownership of the object (retain it). Likewise, __bridge_transfer tells ARC to release the object after the expression.
More about bridge casts
For more details about bridged casts read the section about Bridged Casts and the follow-up section Conversion of retainable object pointers in the LLVM compiler documentation.
Box2D & Chipmunk real-world examples
CCSprite* aSprite = [CCSprite spriteWithFile:@ "image.png" ];
b2BodyDef bodyDef;
bodyDef.userData = (__bridge void *)aSprite;
CCSprite* sprite = (__bridge CCSprite*)body->GetUserData();
cpShape* shape = cpPolyShapeNew(staticBody, numVertices, vertices, offset);
shape->data = (__bridge void *)aSprite;
CCSprite* sprite = (__bridge CCSprite*)shape->data;
|