ARC et distribution pontée

166

Avec ARC, je ne peux plus lancer CGColorRefde casting id. J'ai appris que je devais faire un casting ponté. Selon les documents de Clang :

Une distribution pontée est une distribution de style C annotée avec l'un des trois mots clés suivants:

(__bridge T) opconvertit l'opérande en type de destination T. Si T est un type de pointeur d'objet pouvant être conservé, alors opdoit avoir un type de pointeur non conservable. Si Test un type pointeur non conservable, alors op doit avoir un type pointeur objet conservable. Sinon, le plâtre est mal formé. Il n'y a pas de transfert de propriété et ARC n'insère aucune opération de conservation.

(__bridge_retained T) opconvertit l'opérande, qui doit avoir un type de pointeur d'objet conservable, en type de destination, qui doit être un type de pointeur non conservable. ARC conserve la valeur, sous réserve des optimisations habituelles sur les valeurs locales, et le destinataire est responsable de l'équilibrage de ce +1.

(__bridge_transfer T) opconvertit l'opérande, qui doit avoir un type de pointeur non conservable, en type de destination, qui doit être un type de pointeur d'objet conservable. ARC publiera la valeur à la fin de l'expression complète englobante, sous réserve des optimisations habituelles sur les valeurs locales.

Ces moulages sont nécessaires pour transférer des objets dans et hors du contrôle ARC; voir la justification dans la section sur la conversion des pointeurs d'objets conservables.

Utiliser un __bridge_retainedou un __bridge_transfermoulage uniquement pour convaincre l'ARC d'émettre une retenue ou une libération déséquilibrée, respectivement, est une mauvaise forme.

Dans quel genre de situations utiliserais-je chacun?

Par exemple, CAGradientLayera une colorspropriété qui accepte un tableau de CGColorRefs. Je suppose que je devrais utiliser __brigeici, mais exactement pourquoi je devrais (ou ne devrais pas) n'est pas clair.

Morrowless
la source
17
Avez-vous déjà regardé la session 323 de la WWDC 2011? Cela explique bien mieux l'ARC que je ne le pourrais ici. Il couvre tous les détails du début à la fin. C'est une session incontournable pour tous les développeurs Mac / iOS.
rbrown
Cela pourrait également aider: stackoverflow.com/questions/14352494/…
Ewan Mellor
Lien vers la session WWDC, ce n'était pas anodin à trouver: developer.apple.com/videos/play/wwdc2011/323 - Le bit pertinent est à 23:15
Daniel

Réponses:

215

Je reconnais que la description prête à confusion. Puisque je viens de les saisir, je vais essayer de résumer:

  • (__bridge_transfer <NSType>) opou bien CFBridgingRelease(op)est utilisé pour consommer un compte de rétention d'un CFTypeRefmoment en le transférant vers ARC. Cela pourrait également être représenté parid someObj = (__bridge <NSType>) op; CFRelease(op);

  • (__bridge_retained <CFType>) opou bien CFBridgingRetain(op)est utilisé pour remettre un NSObjectterrain aux FC tout en lui donnant un compte de retenue de +1. Vous devez gérer un que CFTypeRefvous créez de cette manière de la même manière que vous géreriez un résultat CFStringCreateCopy(). Cela pourrait également être représenté parCFRetain((__bridge CFType)op); CFTypeRef someTypeRef = (__bridge CFType)op;

  • __bridgejette juste entre pointeur-terre et Objective-C objet-terre. Si vous n'avez pas envie d'utiliser les conversions ci-dessus, utilisez celle-ci.

C'est peut-être utile. Moi-même, je préfère CFBridging…un peu les macros aux plâtres simples.

monkeydom
la source
Le nombre d'objets retenu est-il augmenté d'arc de 1 lorsque vous utilisez __bridge_transfer? Sinon, il semblerait qu'au moment où CFRelease () est appelé, l'objet soit parti et ne pointe vers rien. De même, lorsque vous utilisez __bridge_retain, ARC réduit-il le nombre de rétention de l'opération de 1? Sinon, il semble que l'objet ne serait jamais correctement libéré.
Tony
2
Une fois sur le territoire de l'ARC, vous ne pensez plus à retenir les comptes, uniquement aux références fortes et faibles.
monkeydom
4
Oui, si vous êtes seulement dans un terrain d'arc fort / faible serait suffisant, mais lorsque vous faites la transition d'objets entre des environnements arc et non-arc, vous devez toujours penser aux implications du nombre de rétention sous le capot
Tony
3
Pas vraiment. Vous devez penser uniquement à entrer et à sortir de l'ARC. Et cela rappelle tout à fait la saisie de l'autoreleasing. (assez intéressant: ARC corrige un modèle courant comme extraire un objet d'un dictionnaire, puis le supprimer avant de l'utiliser, etc.)
monkeydom
3
l'utilisation de l'outil Analyzer (shift + commande + B) peut aider à résoudre ce genre de doutes, car il vous dira dans un langage naturel si le code actuel perd de la mémoire. Si c'est le cas, vous utilisez probablement un plâtre de retenue alors que vous devriez utiliser un plâtre non retenu. si l'analyseur ne vous avertit de rien dans ces lignes de code, vous vous débrouillez probablement bien avec le code actuel
Fabio Napodano
55

J'ai trouvé une autre explication dans la documentation iOS que je pense est plus facile à comprendre:

  • __bridge transfère un pointeur entre Objective-C et Core Foundation sans transfert de propriété.

  • __bridge_retained (CFBridgingRetain)jette un pointeur Objective-C vers un pointeur Core Foundation et vous transfère également la propriété.

    Vous êtes responsable d'appeler CFRelease ou une fonction associée pour renoncer à la propriété de l'objet.

  • __bridge_transfer (CFBridgingRelease)déplace un pointeur non-Objective-C vers Objective-C et transfère également la propriété à l'ARC.

    ARC est responsable de renoncer à la propriété de l'objet.

Source: Types pontés sans frais

Gregschlom
la source
33

Dans ce cas précis, si vous êtes sur iOS, Apple recommande d'utiliser UIColor et sa -CGColorméthode pour renvoyer le CGColorRef dans le colorsNSArray. Dans les notes de mise à jour de la transition vers ARC , sous la section «Le compilateur gère les objets CF renvoyés par les méthodes Cocoa», il est indiqué que l'utilisation d'une méthode comme celle -CGColorqui renvoie un objet Core Foundation sera automatiquement gérée correctement par le compilateur.

Ainsi, ils suggèrent d'utiliser un code comme celui-ci:

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

Notez que pour le moment, l'exemple de code d'Apple ne contient pas le cast (id) que j'ai ci-dessus, ce qui est toujours nécessaire pour éviter une erreur du compilateur.

Brad Larson
la source
Vous pouvez généralement vous en tirer simplement en jetant le premier des objets vers (id) au lieu de tous, si vous préférez.
Philippe Sabourin
1
Cette question concerne la diffusion avec ARC, où le code que vous avez collé n'est pas légal.
Joey Hagedorn
11
@JoeyHagedorn - Vous avez peut-être manqué ma référence à la documentation ARC dans la première phrase de ma réponse, mais non seulement cela est valide sous ARC, c'est l'approche recommandée pour fournir des références CGColorRef dans NSArrays à partir de ces méthodes de conversion UIColor. Moi et beaucoup d'autres, j'utilise ce code exact dans les applications compatibles ARC. La conversion immédiate en (id) à partir d'une méthode qui renvoie un objet Core Foundation relie automatiquement cet objet à ARC correctement.
Brad Larson