ARC y reparto en puente

166

Con ARC, ya no puedo echar CGColorRefa id. Aprendí que necesito hacer un reparto en puente. Según los documentos de clang :

Un reparto en puente es un reparto de estilo C anotado con una de tres palabras clave:

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

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

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

Estos lanzamientos son necesarios para transferir objetos dentro y fuera del control ARC; vea la justificación en la sección sobre conversión de punteros de objetos retenibles.

Usando una __bridge_retainedo __bridge_transferfundido puramente para convencer ARC para emitir retener o liberar una desequilibrada, respectivamente, es la falta de forma.

¿En qué tipo de situaciones usaría cada una?

Por ejemplo, CAGradientLayertiene una colorspropiedad que acepta una matriz de CGColorRefs. Mi suposición es que debería usar __brigeaquí, pero no está claro exactamente por qué debería (o no debería).

Morrowless
fuente
17
¿Ya viste la sesión 323 de WWDC 2011? Eso explica ARC mucho mejor de lo que podría aquí. Cubre todos los detalles de principio a fin. Es una sesión obligada para todos los desarrolladores de Mac / iOS.
rbrown
Esto también podría ayudar: stackoverflow.com/questions/14352494/…
Ewan Mellor
Enlace a la sesión de WWDC, no fue trivial encontrar: developer.apple.com/videos/play/wwdc2011/323 - El bit relevante es a las 23:15
Daniel

Respuestas:

215

Estoy de acuerdo en que la descripción es confusa. Como acabo de comprenderlos, intentaré resumir:

  • (__bridge_transfer <NSType>) opo, alternativamente, CFBridgingRelease(op)se usa para consumir un conteo de retención de un CFTypeReftiempo mientras se transfiere a ARC. Esto también podría estar representado porid someObj = (__bridge <NSType>) op; CFRelease(op);

  • (__bridge_retained <CFType>) opo, alternativamente, CFBridgingRetain(op)se usa para entregar una NSObjecttierra CF mientras le da un conteo de retención de +1. Debe manejar una CFTypeRefcreación de esta manera de la misma manera que manejaría un resultado CFStringCreateCopy(). Esto también podría estar representado porCFRetain((__bridge CFType)op); CFTypeRef someTypeRef = (__bridge CFType)op;

  • __bridgesolo se lanza entre puntero-tierra y Objective-C object-land. Si no tiene inclinación a usar las conversiones anteriores, use esta.

Quizás esto sea útil. Yo mismo, prefiero las CFBridging…macros bastante por encima de los modelos simples.

monkeydom
fuente
¿Los objetos que retienen el recuento aumentan en arco en 1 cuando usas __bridge_transfer? De lo contrario, parecería que en el momento en que se llama CFRelease (), el objeto desaparece y no apunta a nada. Del mismo modo, cuando usa __bridge_retain, ¿ARC reduce el recuento de retención de operaciones en 1? De lo contrario, parece que el objeto nunca se liberaría correctamente.
Tony
2
Una vez en ARC land ya no piensas en retener conteos, solo en referencias fuertes y débiles.
monkeydom
44
Sí, si solo está en tierra de arco fuerte / débil sería suficiente, sin embargo, cuando está haciendo la transición de objetos entre entornos de arco y sin arco, aún tiene que pensar en las implicaciones de retención de conteos bajo el capó
Tony
3
Realmente no. Debe pensar en entrar y salir de la tierra ARC. Y esto es bastante alentador de comprender el autorreleasing. (Curiosamente: ARC corrige un patrón común como sacar un objeto de un diccionario y luego eliminarlo antes de usarlo, etc.)
monkeydom
3
El uso de la herramienta Analizador (shift + comando + B) puede ayudar a resolver este tipo de dudas, ya que le informará en un lenguaje natural si el código actual tiene una pérdida de memoria. Si lo hace, probablemente esté usando un yeso de retención, mientras que debería usar un yeso sin retención. si el Analizador no le advierte sobre nada en esas líneas de código, probablemente le esté yendo bien con el código actual
Fabio Napodano
55

Encontré otra explicación en la documentación de iOS que creo que es más fácil de entender:

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

  • __bridge_retained (CFBridgingRetain)lanza un puntero 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_transfer (CFBridgingRelease)mueve un puntero que no es Objective-C a Objective-C y también transfiere la propiedad a ARC.

    ARC es responsable de renunciar a la propiedad del objeto.

Fuente: Tipos puenteados gratuitos

gregschlom
fuente
33

Como continuación, en este caso específico, si está en iOS, Apple recomienda usar UIColor y su -CGColormétodo para devolver el CGColorRef al colorsNSArray. En las Notas de lanzamiento de Transición a ARC , en la sección "El compilador maneja los objetos CF devueltos de los métodos de cacao", se indica que utilizando un método como-CGColor el compilador manejará automáticamente el que devuelve un objeto Core Foundation.

Por lo tanto, sugieren usar código como el siguiente:

CAGradientLayer *gradientLayer = (CAGradientLayer *)[self layer];
gradientLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor darkGrayColor] CGColor],
                                                 (id)[[UIColor lightGrayColor] CGColor], nil];

Tenga en cuenta que a partir de ahora, al código de ejemplo de Apple le falta el elenco (id) que tengo arriba, que todavía es necesario para evitar un error del compilador.

Brad Larson
fuente
Por lo general, puede salirse con solo lanzar el primer objeto a (id) en lugar de a todos, si lo prefiere.
Philippe Sabourin el
1
Esta pregunta se refiere a la transmisión con ARC, donde el código que ha pegado no es legal.
Joey Hagedorn
11
@JoeyHagedorn: tal vez se haya perdido mi referencia a la documentación ARC en la primera oración de mi respuesta, pero no solo es válida bajo ARC, es el enfoque recomendado para proporcionar referencias CGColorRef en NSArrays de estos métodos de conversión UIColor. Yo, y muchos otros, utilizamos este código exacto dentro de las aplicaciones habilitadas para ARC. La conversión inmediata a (id) desde un método que devuelve un objeto de Core Foundation automáticamente conecta ese objeto a ARC correctamente.
Brad Larson