ARC e elenco em ponte

166

Com o ARC, não posso mais transmitir CGColorRefpara id. Eu aprendi que preciso fazer um elenco em ponte. De acordo com clang docs :

Um elenco em ponte é um elenco no estilo C anotado com uma das três palavras-chave:

(__bridge T) oplança o operando no tipo de destino T. Se T é um tipo de ponteiro de objeto retido, opdeve ter um tipo de ponteiro não retido. Se Tfor um tipo de ponteiro não retido, op deve ter um tipo de ponteiro de objeto retido. Caso contrário, o elenco está mal formado. Não há transferência de propriedade e o ARC não insere operações de retenção.

(__bridge_retained T) opconverte o operando, que deve ter o tipo de ponteiro de objeto retido, no tipo de destino, que deve ser um tipo de ponteiro não retido. O ARC retém o valor, sujeito às otimizações usuais nos valores locais, e o destinatário é responsável por equilibrar esse +1.

(__bridge_transfer T) opconverte o operando, que deve ter um tipo de ponteiro não retido, no tipo de destino, que deve ser um tipo de ponteiro de objeto retido. O ARC liberará o valor no final da expressão completa anexa, sujeito às otimizações usuais nos valores locais.

Esses lançamentos são necessários para transferir objetos para dentro e fora do controle do ARC; veja a justificativa na seção sobre conversão de ponteiros de objetos retidos.

O uso de um __bridge_retainedou __bridge_transferelenco puramente para convencer o ARC a emitir uma retenção ou liberação desequilibrada, respectivamente, é uma forma ruim.

Em que tipo de situações eu usaria cada uma?

Por exemplo, CAGradientLayerpossui uma colorspropriedade que aceita uma matriz de CGColorRefs. Meu palpite é que eu deveria usar __brigeaqui, mas exatamente por que eu deveria (ou não) não é claro.

Morrowless
fonte
17
Você já assistiu a sessão 323 da WWDC 2011? Isso explica o ARC muito melhor do que eu poderia aqui. Abrange todos os detalhes do começo ao fim. É uma sessão obrigatória para todos os desenvolvedores de Mac / iOS.
Rbrown
Isso também pode ajudar: stackoverflow.com/questions/14352494/…
Ewan Mellor
Link para a sessão da WWDC, não foi trivial encontrar: developer.apple.com/videos/play/wwdc2011/323 - O bit relevante é às 23:15
Daniel

Respostas:

215

Concordo que a descrição é confusa. Desde que eu as compreendi, tentarei resumir:

  • (__bridge_transfer <NSType>) opou, alternativamente, CFBridgingRelease(op)é usado para consumir uma contagem de retenção de um CFTypeRefenquanto a transfere para o ARC. Isso também pode ser representado porid someObj = (__bridge <NSType>) op; CFRelease(op);

  • (__bridge_retained <CFType>) opou, alternativamente, CFBridgingRetain(op)é usado para entregar um NSObjectterreno CF, dando a ele uma contagem de retenção +1. Você deve lidar com um que CFTypeRefvocê cria dessa maneira da mesma forma como lidaria com um resultado CFStringCreateCopy(). Isso também pode ser representado porCFRetain((__bridge CFType)op); CFTypeRef someTypeRef = (__bridge CFType)op;

  • __bridgeapenas projeta entre terra do ponteiro e terra do objeto C-objetivo. Se você não tem inclinação para usar as conversões acima, use esta.

Talvez isso seja útil. Eu mesmo prefiro as CFBridging…macros um pouco do que os modelos simples.

monkeydom
fonte
Os objetos que mantêm a contagem aumentam em 1 o arco quando você usa __bridge_transfer? Caso contrário, parece que no momento em que CFRelease () é chamado, o objeto desaparece e aponta para nada. Da mesma forma, quando você usa __bridge_retain, o ARC reduz a contagem de retenção de operações em 1? Caso contrário, parece que o objeto nunca seria liberado corretamente.
26411 Tony
2
Uma vez na terra da ARC, você não pensa mais em manter contagens, apenas em referências fortes e fracas.
monkeydom
4
Sim, se você estiver apenas em terra firme, forte / fraca, seria suficiente; no entanto, quando você estiver fazendo a transição de objetos entre ambientes com e sem arco, ainda precisará pensar nas implicações da contagem de retenções
Tony
3
Na verdade não. Você precisa pensar em entrar e sair das terras da ARC. E isso é bastante reminiscente de obter autoreleasing. (curiosamente: ARC corrige um padrão comum como a obtenção de um objeto a partir de um dicionário e, em seguida, removê-lo antes de usá-lo, etc.)
monkeydom
3
o uso da ferramenta Analisador (shift + command + B) pode ajudar a solucionar esse tipo de dúvida, pois será informado em um idioma natural se o código atual estiver vazando memória. Se isso acontecer, você provavelmente está usando um elenco de retenção enquanto deve usar um elenco sem retenção. Se o analisador não avisá-lo sobre qualquer coisa em que as linhas de código, provavelmente você está fazendo bem com o código atual
Fabio Napodano
55

Encontrei outra explicação na documentação do iOS que acho mais fácil de entender:

  • __bridge transfere um ponteiro entre Objective-C e Core Foundation sem transferência de propriedade.

  • __bridge_retained (CFBridgingRetain) lança um ponteiro Objective-C para uma Core Foundation e também transfere a propriedade para você.

    Você é responsável por chamar CFRelease ou uma função relacionada para renunciar à propriedade do objeto.

  • __bridge_transfer (CFBridgingRelease)move um ponteiro não-Objective-C para Objective-C e também transfere a propriedade para o ARC.

    O ARC é responsável por renunciar à propriedade do objeto.

Fonte: Tipos de pontes gratuitas

gregschlom
fonte
33

Como conseqüência, nesse caso específico, se você estiver no iOS, a Apple recomenda usar o UIColor e seu -CGColormétodo para retornar o CGColorRef ao colorsNSArray. Nas notas de versão Transição para ARC , na seção "O compilador manipula objetos CF retornados de métodos de cacau", é indicado que o uso de um método como-CGColor que retorna um objeto Core Foundation será automaticamente manipulado corretamente pelo compilador.

Assim, eles sugerem o uso de código como o seguinte:

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

Observe que, no momento, o código de exemplo da Apple está ausente da conversão (id) que tenho acima, o que ainda é necessário para evitar um erro do compilador.

Brad Larson
fonte
Normalmente, você pode simplesmente lançar o primeiro dos objetos para (id) em vez de todos, se preferir.
Philippe Sabourin
1
Esta pergunta é sobre a transmissão com o ARC, onde o código que você colou não é legal.
Joey Hagedorn
11
@ JoeyHagedorn - Talvez você tenha perdido minha referência à documentação do ARC na primeira frase da minha resposta, mas isso não só é válido no ARC, mas também é a abordagem recomendada para fornecer referências CGColorRef no NSArrays a partir desses métodos de conversão UIColor. Eu e muitos outros utilizamos esse código exato em aplicativos habilitados para ARC. A conversão imediata para (id) de um método que retorna um objeto do Core Foundation faz a ponte automática desse objeto para o ARC corretamente.
Brad Larson