14

Sto lavorando con Box2D (C++) e creo un oggetto Objective-C e lo assegno alla proprietà userData del corpo di Box2D, che è di tipo void*.Come archiviare in modo sicuro un oggetto id in un membro void * di C++ sotto ARC quando nessun altro riferimento rimane sull'oggetto?

Ora in alcuni casi il void* userData può essere l'unico riferimento attivo a quell'oggetto ObjC. Pertanto, poiché ho utilizzato (__bridge void*) nel compito, ARC si sta lasciando andare. È qualcosa che devo aggiustare.

Ho riflettuto sulle opzioni per evitare che ciò accada? Ho letto Clang's ARC documentation, in particolare le parti relative al lancio del ponte (così come Q & A su SO) oltre ad annuire ai vari costrutti di colata del ponte che considerano "mal formata".

Tuttavia, il mio primo pensiero è stato quello di utilizzare (__bridge_retained void*) nell'assegnazione iniziale a userData. Ma questo mi ha fatto chiedere come bilanciare ciò che mantiene? Ovviamente non posso inviare il rilascio all'oggetto.

Quindi dovrei CFRelease() l'oggetto? O dovrebbe essere CFBridgingRelease()? O sono entrambi illegali qui?

È un cast userData un ID temporaneo sufficiente, forse durante l'impostazione di userData su NULL in seguito? E 'anche una buona idea?

so l'alternativa sarebbe quella di mantenere un separato NSArray/NSDictionary per le userData oggetti e tenerli in sincronia con il ciclo di vita del corpo Box2D, l'aggiunta e la rimozione di essi in sintonia con i loro corpi Box2D.

Ma questo si sente come eccessivo perché qui io so quello che sto facendo, lo so che ho bisogno di +1 l'oggetto il più a lungo il corpo Box2D è attivo, e -1 l'oggetto quando il corpo Box2D viene rimosso. Inoltre, so che ci sono solo due metodi in cui i corpi Box2D vengono aggiunti e rimossi, e l'accesso diretto a userData non è nemmeno possibile nel mio framework perché tutti gli oggetti Box2D sono nascosti dietro le interfacce/wrapper Objective-C.

Mettendo da parte un "malformato" per un momento, cosa consiglieresti che dovrei fare in questa situazione?

+0

@Emil: grazie per aver corretto il codice inline, stavo proprio per farlo da solo. – LearnCocos2D

+2

Nessun problema, devo ammettere che è stato strano montare una cosa così banale nel post di un utente 20k! Ha: D – Emil

+0

sì, a volte pongo domande davvero sconsiderate :) – LearnCocos2D

risposta

23

__bridge_retained significa "Invia questo oggetto ARC in terra non ARC mantenendolo". Chiami questo quando hai bisogno di creare un "non tracciato" void *. Quindi, nel tuo caso, userData = (__bridge_retained void *)obj.

__bridge_transfer significa "Estrarre questo oggetto dalla terra senza ARC rilasciandolo". Chiama questo quando vuoi invalidare efficacemente lo void *. Quindi, obj = (__bridge_transfer id)userData. Dopo questo, il puntatore userData non è sicuro da usare; al contrario, si lavora solo con obj. Quando obj esce dallo scope, ARC lo rilascerà per l'ultima volta. Ciò può comportare la creazione di un valore temporaneo id esclusivamente per questo scopo.

Quindi, nel tuo caso, si desidera utilizzare __bridge_retained quando si spedisce l'oggetto in Box2D e si utilizza __bridge_transfer quando si desidera invalidare lo userData. Se è necessario accedere allo userData come oggetto Objective-C ma non invalidare il puntatore, utilizzare lo standard __bridge.

+2

Eccellente, chiara spiegazione, +1. –

+1

Buono. Ero principalmente confuso dal ricondurlo perché non sembra "fare nulla" all'oggetto, quando invece lo fa. Per quanto riguarda lo stile, penso che preferirò: CFBridgingRelease (body-> GetUserData()) seguito da body-> SetUserData (NULL). Da quello che capisco è la stessa cosa del cast __bridge_transfer. – LearnCocos2D

+0

In realtà userò '__bridge_ *', perché questo non interessa in alcun modo CF. – nneonneo

2

Hai frainteso ciò che l'autore della documentazione intendeva per "mal formato".Questo è mal-formato:

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

Anche questo è mal formato:

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

Questo non è mal-formato:

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

di non essere malformati è sufficiente che il valore di sinistra è conservabile e il valore corretto non è conservabile o viceversa. Quindi dal momento che nel tuo caso stai lanciando puntatori a oggetti a puntatori grezzi e viceversa, il tuo approccio è corretto.

A casa mia implementerei un puntatore intelligente che invia un messaggio CFBridgingRetain sulla costruzione e un messaggio CFBridgingRelease sulla distruzione.

Problemi correlati