Dónde y cómo __bridge

Necesito un consejo sobre __bridge -ing en iOS.

Con suerte, el SSCCE 1 a continuación explicará el problema mejor que yo con palabras, pero necesito saber cómo puedo convertir un void* en un NSMutableArray* ; qué variación __bridge debería usarse (ver comentario en el código).

Al leer sobre los diferentes puentes, __bridge_transfer que necesitaría __bridge_transfer pero luego recibí un EXC_BAD_ACCESS en addObject:

En última instancia, me gustaría tener una matriz de CGPoints en el CGPath después de que se haya llamado a CGPathApply .

 #import <Foundation/Foundation.h> void _processPathElement(void* info, const CGPathElement* element) { NSMutableArray *array = (/* WHAT BRIDGE HERE */ NSMutableArray*) info; switch (element->type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: { CGPoint point = element->points[0]; [array addObject:[NSValue valueWithCGPoint:point]]; break; } default: break; } } int main(int argc, char *argv[]) { @autoreleasepool { //Create path CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint( path, NULL, 0, 0); CGPathAddLineToPoint(path, NULL, 1, 0); CGPathAddLineToPoint(path, NULL, 1, 1); CGPathAddLineToPoint(path, NULL, 0, 1); CGPathCloseSubpath(path); NSMutableArray *pathPoints = [NSMutableArray array]; CGPathApply(path, &pathPoints, _processPathElement); NSLog(@"Points:%@", pathPoints); } } 

1: SSCCE

La documentation sobre el uso de la palabra key bridge se puede encontrar aquí . Específicamente, quiero señalar §3.2.4:

(__bridge T) op el operando al tipo de destino T. Si T es un tipo de puntero de object retenible, entonces op debe tener un tipo de puntero no retenible. Si T es un tipo de puntero no retenible, entonces op debe tener un tipo de puntero de object retenible. De lo contrario, el yeso está mal formado. No hay transferencia de propiedad, y ARC no inserta operaciones de retención.

(__bridge_retained T) op el operando, que debe tener un tipo de puntero de object retenible, al tipo de destino, que debe ser un tipo de puntero no retenible. ARC conserva el valor, sujeto a las optimizaciones habituales de los valores locales, y el destinatario es responsable de equilibrar ese +1.

(__bridge_transfer T) op arroja el operando, que debe tener un tipo de puntero no retenible, al tipo de destino, que debe ser un tipo de puntero de object retenible. ARC lanzará el valor al final de la expresión completa adjunta, sujeto a las optimizaciones habituales en los valores locales.

El puntero al que se le pasa ( void* ) es un tipo de puntero no retenible, mientras que su NSMutableArray es un tipo de puntero retenedor. Esto descarta __bridge_retained inmediato. Entonces, la pregunta es, ¿para __bridge o para __bridge_transfer ?

__bridge_transfer se utiliza normalmente cuando desea que el puntero Objective-C provenga de un método que devuelve un object CF que se ha conservado. Por ejemplo, CFStringCreateWithFormat devolverá un CFString retenido, pero si quiere un NSString de él, necesita __bridge_transfer entre ellos. Esto hará que ARC libere el object que CF retuvo cuando sea apropiado. Por ejemplo, NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

Su código no lo está haciendo, no necesita entrometerse con la propiedad. Su método principal controla la gestión de su memory y simplemente pasa una reference a un método al que llama (aunque indirectamente, pero está todo dentro del scope de la principal). Como tal, usaría __bridge .

Pero espera, cuando uso __bridge, ¡mi código tiene errores de acceso a la memory !?

Ah, este es un problema con el código que publicaste, y no está relacionado con toda la discusión puente. Debe pasar un void* a CGApplyPath, para su function de procesamiento _processPathElement . Lo que está pasando es NSMutableArray** .

Cuando vuelves a la NSMutableArray* , estás realmente lanzando un NSMutableArray** . Esto causará el infame EXC_BAD_ACCESS. Debe pasar el puntero mismo, no un puntero a un puntero. Pero , CGPathApply(path, pathPoints, _processPathElement) no funcionará, no puede pasar un NSMutableArray* como un void* . Lo que necesitas (irónicamente) es un puente. Por las mismas razones que antes, todo lo que necesitas es __bridge . Vea a continuación el código, con los puentes correctos en su lugar, y funcionando como se esperaba:

 #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> void _processPathElement(void* info, const CGPathElement* element) { NSMutableArray *array = (__bridge NSMutableArray*) info; switch (element->type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: { CGPoint point = element->points[0]; [array addObject:[NSValue valueWithCGPoint:point]]; break; } default: break; } } int main(int argc, char *argv[]) { @autoreleasepool { //Create path CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint( path, NULL, 0, 0); CGPathAddLineToPoint(path, NULL, 1, 0); CGPathAddLineToPoint(path, NULL, 1, 1); CGPathAddLineToPoint(path, NULL, 0, 1); CGPathCloseSubpath(path); NSMutableArray *pathPoints = [[NSMutableArray alloc] init]; CGPathApply(path, (__bridge void*)pathPoints, _processPathElement); NSLog(@"Points:%@", pathPoints); } } 

Esto se imprimirá:

 Points:( "NSPoint: {0, 0}", "NSPoint: {1, 0}", "NSPoint: {1, 1}", "NSPoint: {0, 1}" ) 

No estoy realmente seguro de por qué esto funciona, pero he encontrado que la solución es:

 NSMutableArray *array = (__bridge NSMutableArray*) info; //AND CGPathApply(path, (__bridge void*)pathPoints, _processPathElement); 

Si alguien puede explicar por qué funciona esto y confirmar que no hay (o hay) pérdidas de memory, estaría agradecido.