14

Ich arbeite mit Box2D (C++) und ich erstelle ein Objective-C-Objekt und weisen Sie es einem Box2D-Körper userData -Eigenschaft, die vom Typ void* ist.Wie kann ein ID-Objekt in einem C++ - void * -Member unter ARC sicher gespeichert werden, wenn keine anderen Verweise auf das Objekt bestehen?

Jetzt kann in einigen Fällen die void* userData die einzige aktive Referenz auf dieses ObjC-Objekt sein. Deshalb, weil ich (__bridge void*) in der Zuordnung verwendet habe, lässt ARC es los. Das muss ich beheben.

Ich habe über die Optionen nachgedacht, um dies zu verhindern? Ich lese Clang's ARC documentation, speziell die Teile über Brückenguss (sowie Q & A auf SO) sowie Nicken zu den verschiedenen Brückenguss-Konstrukte, die sie als "schlecht geformt" betrachten.

Noch war mein erster Gedanke, (__bridge_retained void*) in der ursprünglichen Zuordnung zu userData zu verwenden. Aber das brachte mich dazu, mich zu fragen, wie man das ausgleicht? Ich kann offensichtlich keine Freigabe an das Objekt senden.

Also müsste ich CFRelease() das Objekt? Oder müsste es CFBridgingRelease() sein? Oder sind beide hier illegal?

Ist ein (__bridge_transfer void*) Umwandlung von userData zu einem temporären ID-Typ genug, vielleicht beim Einstellen von userData auf NULL hinterher? Ist das überhaupt eine gute Idee?

weiß, dass ich die Alternative ein separates NSArray/NSDictionary für die userData Objekte und sie mit der Lebensdauer des Box2D Körpers synchron halten zu halten wäre, das Hinzufügen und sie synchron mit ihrem Box2D Körper zu entfernen.

Aber das fühlt sich an wie viel des Guten, weil hier ich weiß, was ich tue, ich weiß, ich muss so lange das Objekt +1 für so der Box2D Körper aktiv ist, und -1 das Objekt, wenn der Box2D Körper entfernt wird. Außerdem weiß ich, dass es nur zwei Methoden gibt, mit denen die Box2D-Körper hinzugefügt und entfernt werden, und der direkte Zugriff auf userData ist in meinem Framework nicht möglich, da alle Box2D-Objekte hinter Objective-C-Schnittstellen/Wrappern verborgen sind.

Vielleicht einen Moment lang "schlecht gebildet" beiseite legen, was würden Sie mir empfehlen, sollte ich in dieser Situation tun?

+0

@Emil: Danke für die Korrektur des Inline-Codes, ich wollte es gerade selbst machen. – LearnCocos2D

+2

Kein Problem, ich muss zugeben, dass es seltsam war, solch eine triviale Sache in einem 20k-User Post zu bearbeiten! Ha: D – Emil

+0

Ja manchmal frage ich wirklich stoopid Fragen :) – LearnCocos2D

Antwort

23

__bridge_retained bedeutet "Schicken Sie dieses ARC-Objekt in ein Nicht-ARC-Land, indem Sie es beibehalten". Sie rufen dies auf, wenn Sie ein "nicht verfolgtes" void * erstellen müssen. Also, in Ihrem Fall, userData = (__bridge_retained void *)obj.

__bridge_transfer bedeutet "Ziehen Sie dieses Objekt aus dem Nicht-ARC-Land zurück, indem Sie es loslassen". Sie rufen dies auf, wenn Sie die void * effektiv ungültig machen möchten. Also, obj = (__bridge_transfer id)userData. Danach ist der Zeiger userData nicht sicher zu verwenden; Stattdessen arbeiten Sie nur mit obj. Wenn obj den Gültigkeitsbereich verlässt, wird ARC es zum letzten Mal freigeben. Dies kann dazu führen, dass nur für diesen Zweck ein temporärer id erstellt wird.

Also, in Ihrem Fall möchten Sie __bridge_retained verwenden, wenn Sie das Objekt in Box2D versenden, und __bridge_transfer verwenden, wenn Sie die userData ungültig machen möchten. Wenn Sie auf das userData als Objective-C-Objekt zugreifen müssen, den Zeiger jedoch nicht ungültig machen, verwenden Sie einfach __bridge.

+2

Hervorragende, klare Erklärung, +1. –

+1

Gut. Ich war hauptsächlich verwirrt darüber, es zurückzuwerfen, weil es dem Objekt scheinbar nichts "antut", obwohl es tatsächlich so ist. Stilistisch denke ich, dass ich bevorzuge: CFBridgingRelease (body-> GetUserData()) gefolgt von body-> SetUserData (NULL). Von dem, was ich verstehe, ist das dasselbe wie __bridge_transfer cast. – LearnCocos2D

+0

Ich würde eigentlich '__bridge_ *' selbst verwenden, da dies CF in keiner Weise wirklich betrifft. – nneonneo

2

Sie haben missverstanden, was der Autor der Dokumentation für "schlecht gebildet" bedeutet.Dies ist schlecht gebildet:

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

Auch diese schlecht gebildet:

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

Dies ist nicht schlecht gebildet:

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

nicht sein schlecht ausgebildet ist genug, dass der linke Wert ist beibehalten und der richtige Wert kann nicht beibehalten werden, oder umgekehrt. Da in Ihrem Fall Objektzeiger auf rohe Zeiger und umgekehrt übertragen werden, ist Ihr Ansatz korrekt.

An Ihrer Stelle würde ich einen Smart-Pointer implementieren, der eine CFBridgingRetain-Nachricht bei der Konstruktion und eine CFBridgingRelease-Nachricht bei der Zerstörung sendet.

Verwandte Themen