NSString a CFStringRef y CFStringRef a NSString en ARC?

87

Estoy tratando de comprender la forma correcta de conseguir una NSStringde una CFStringRefde ARC? Lo mismo para ir en la dirección opuesta, CFStringRefa NSStringARC?

¿Cuál es la forma correcta de hacer esto sin crear pérdidas de memoria?

zumzum
fuente
4
CFStringRef foo (__bridge CFStringRef)theNSString;yNSString *bar = (__bridge NSString *)theCFString;
¿Podría explicar en detalle qué está sucediendo realmente cuando se utilizan esas dos opciones?
zumzum
No exactamente. No uso ARC, así que todo lo que sé es que tienes que hacer esto, pero no el por qué.
1
Se suponía que @GabrielePetronella ARC haría la codificación fácil, el código más corto y más legible y reduciría la posibilidad de errores humanos. Entonces, ahora en lugar de tener que ocuparnos de los recuentos de referencias mediante objetos retaining y release-ing, ahora debemos usar moldes "hermosos" como __bridge_transfer, __unsafe_unretainedy __autoreleasing. Nadie no tiene tiempo para eso. (Y en serio, es más difícil de leer. En mi opinión, no facilitó la gestión de la memoria en absoluto).
1
@ H2CO3 gracias por la respuesta. Estoy totalmente en desacuerdo, especialmente con la última oración, pero respeto tu punto de vista :)
Gabriele Petronella

Respuestas:

177

Típicamente

NSString *yourFriendlyNSString = (__bridge NSString *)yourFriendlyCFString;

y

CFStringRef yourFriendlyCFString = (__bridge CFStringRef)yourFriendlyNSString;

Ahora, si quiere saber por qué la __bridgepalabra clave está ahí, puede consultar la documentación de Apple . Allí encontrará:

__bridge transfiere un puntero entre Objective-C y Core Foundation sin transferencia de propiedad.

__bridge_retainedo CFBridgingRetainlanza un puntero de Objective-C a un puntero de Core Foundation y también le transfiere la propiedad. Usted es responsable de llamar a CFRelease o una función relacionada para renunciar a la propiedad del objeto.

__bridge_transfero CFBridgingReleasemueve un puntero que no es de Objective-C a Objective-C y también transfiere la propiedad a ARC. ARC es responsable de renunciar a la propiedad del objeto.

Lo que significa que en los casos anteriores estás lanzando el objeto sin cambiar la propiedad. Esto implica que en ningún caso serás el encargado de manejar la memoria de las cadenas.

También puede darse el caso de que desee transferir la propiedad por algún motivo.

Por ejemplo, considere el siguiente fragmento

- (void)sayHi {
    CFStringRef str = CFStringCreateWithCString(NULL, "Hello World!", kCFStringEncodingMacRoman);

    NSString * aNSString = (__bridge NSString *)str;

    NSLog(@"%@", aNSString);

    CFRelease(str); //you have to release the string because you created it with a 'Create' CF function
}

en tal caso, es posible que desee guardar un CFReleasetransfiriendo la propiedad al transmitir.

- (void)sayHi {
    CFStringRef str = CFStringCreateWithCString(NULL, "Hello World!", kCFStringEncodingMacRoman);

    NSString * aNSString = (__bridge_transfer NSString *)str;
// or alternatively
    NSString * aNSString = (NSString *)CFBridgingRelease(str);

    NSLog(@"%@", aNSString);
}

Se strha transferido la propiedad de , por lo que ahora ARC se activará y liberará la memoria por usted.

Por otro lado, puedes lanzar un NSString *a CFStringusando un __bridge_retainedyeso, de modo que serás dueño del objeto y tendrás que liberarlo explícitamente usando CFRelease.


Para envolverlo puedes tener

NSString → CFString

// Don't transfer ownership. You won't have to call `CFRelease`
CFStringRef str = (__bridge CFStringRef)string;

// Transfer ownership (i.e. get ARC out of the way). The object is now yours and you must call `CFRelease` when you're done with it
CFStringRef str = (__bridge_retained CFStringRef)string // you will have to call `CFRelease`

CFString → NSString

// Don't transfer ownership. ARC stays out of the way, and you must call `CFRelease` on `str` if appropriate (depending on how the `CFString` was created)
NSString *string = (__bridge NSString *)str;

// Transfer ownership to ARC. ARC kicks in and it's now in charge of releasing the string object. You won't have to explicitly call `CFRelease` on `str`
NSString *string = (__bridge_transfer NSString *)str;
Gabriele Petronella
fuente
Muchas gracias, esto no es realmente intuitivo, pero gracias a ti, lección aprendida
Sulfkain
@: pequeña pregunta. así que si usamos ARC. cuándo NSString->CFString, deberíamos usar __bridge. pero cuando CFString->NSStringdeberíamos usar __bride_transfer. ? Y cualquier efecto secundario, si lo usamos CFReleasecuando no lo necesitamos. gracias :)
hqt
@hqt, si quieres la forma 'fácil', sí, lo que dices es correcto. Además, un extra CFReleasedebería bloquear razonablemente su programa, ya que terminará con una operación de retención / liberación no coincidente, y eventualmente liberará un NULLpuntero.
Gabriele Petronella