Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to safely store an id object in a C++ void* member under ARC when no other references hold on to the object?

I'm working with Box2D (C++) and I create an Objective-C object and assign it to a Box2D body's userData property, which is of type void*.

Now in some cases the void* userData may be the only active reference to that ObjC object. Therefore, because I used (__bridge void*) in the assignment, ARC is letting it go. That's something I need to fix.

I've been pondering the options to prevent this from happening? I read Clang's ARC documentation, specifically the parts about bridge casting (as well as Q&A on SO) as well as nodding to the various bridge casting constructs they consider to be "ill-formed".

Still, my first thought was to use (__bridge_retained void*) in the initial assignment to userData. But that made me wonder how to balance that retain? I obviously can't send release to the object.

So would I have to CFRelease() the object? Or would it need to be CFBridgingRelease()? Or are both illegal here?

Is a (__bridge_transfer void*) cast from userData to a temporary id type enough, perhaps while setting userData to NULL afterwards? Is that even a good idea?

I know the alternative would be to keep a separate NSArray/NSDictionary for the userData objects and keep them in synch with the lifetime of the Box2D body, adding and removing them in synch with their Box2D bodies.

But this feels like overkill because here I know what I'm doing, I know I need to +1 the object for as long as the Box2D body is active, and -1 the object when the Box2D body is removed. Plus I know that there are only two methods where the Box2D bodies are being added and removed, and direct access to userData is not even possible in my framework because all Box2D objects are hidden behind Objective-C interfaces/wrappers.

Setting possibly "ill-formed" aside for a moment, what would you recommend I should do in this situation?

like image 363
LearnCocos2D Avatar asked Feb 08 '13 22:02

LearnCocos2D


2 Answers

__bridge_retained means "Send this ARC object off into no-ARC land by retaining it". You call this when you need to create an "untracked" void *. So, in your case, userData = (__bridge_retained void *)obj.

__bridge_transfer means "Pull this object back from no-ARC land by releasing it". You call this when you want to effectively invalidate the void *. So, obj = (__bridge_transfer id)userData. After this, the userData pointer is not safe to use; instead, you work only with obj. When obj goes out of scope, ARC will release it for the last time. This may entail creating a temporary id solely for this purpose.

So, in your case, you do want to use __bridge_retained when you ship the object off into Box2D, and use __bridge_transfer when you want to invalidate the userData. If you need to access the userData as an Objective-C object but not invalidate the pointer, use plain __bridge.

like image 169
nneonneo Avatar answered Sep 28 '22 08:09

nneonneo


You misunderstood what the author of the documentation was meaning for "ill-formed". This is ill-formed:

NSData* data; // Initialized
NSData* data2= (__bridge NSData*) data;

Also this is ill-formed:

void* data; // Initialized
void* data2= (__bridge void*) data;

This is not ill-formed:

NSData* data; // Initialized
void* data2= (__bridge void*) data;

To be not ill-formed is enough that the left value is retainable and the right value is not retainable, or viceversa. So since in your case your're casting object pointers to raw pointers and viceversa, your approach is correct.

At your place I'd implements a smart pointer that sends a CFBridgingRetain message on construction, and a CFBridgingRelease message on destruction.

like image 44
Ramy Al Zuhouri Avatar answered Sep 28 '22 09:09

Ramy Al Zuhouri