web-dev-qa-db-fra.com

Où et comment __bridge

J'ai besoin de conseils sur __bridge- dans iOS.

Espérons que la SSCCE1 ci-dessous expliquera le problème mieux que je ne peux le dire, mais j'ai besoin de savoir comment convertir un void* à un NSMutableArray*; lequel __bridge une variation doit être utilisée (voir le commentaire dans le code).

En lisant les différents ponts, j'ai déduit que j'aurais besoin de __bridge_transfer mais je reçois un EXC_BAD_ACCESS le addObject:

En fin de compte, j'aimerais avoir un tableau de CGPoints dans CGPath après l'appel de 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

31
James Webster

La documentation sur l'utilisation du mot-clé bridge peut être trouvée ici . Plus précisément, je tiens à souligner le §3.2.4:

(__bridge T) op Convertit l'opérande en type de destination T. Si T est un type de pointeur d'objet conservable, alors op doit avoir un type de pointeur non conservable. Si T est un type de pointeur non conservable, alors op doit avoir un type de pointeur d'objet conservable. Sinon, le casting est mal formé. Il n'y a aucun transfert de propriété et ARC n'insère aucune opération de conservation.

(__bridge_retained T) op Convertit 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) op Convertit 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 libérera la valeur à la fin de l'expression complète englobante, sous réserve des optimisations habituelles sur les valeurs locales.

Le pointeur dans lequel vous passez (void*) Est un type de pointeur non conservable, tandis que votre NSMutableArray est un type de pointeur conservable. Cela exclut immédiatement __bridge_retained. La question est donc de __bridge Ou de __bridge_transfer?

__bridge_transfer Est généralement utilisé lorsque vous souhaitez que le pointeur Objective-C d'une méthode qui renvoie un objet CF qui a été conservé. Par exemple, CFStringCreateWithFormat renverra une CFString conservée, mais si vous en voulez une NSString, vous devez __bridge_transfer Entre eux. Cela fera en sorte qu'ARC libère l'objet que CF a conservé le cas échéant. Par exemple, NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

Votre code ne fait pas cela, vous n'avez pas besoin de vous mêler de la propriété. Votre méthode principale contrôle la gestion de sa mémoire et transmet simplement une référence à une méthode qu'elle appelle (bien qu'indirectement, mais tout est dans le cadre de main). En tant que tel, vous utiliseriez __bridge.

Mais attendez, lorsque j'utilise __bridge, mon code obtient des erreurs d'accès à la mémoire!?

Ah, c'est un problème avec le code que vous avez publié, et ce n'est pas en relation avec toute la discussion de transition. Vous devez passer un void* À CGApplyPath, pour votre fonction de traitement _processPathElement. Ce que vous passez est NSMutableArray**.

Lorsque vous effectuez une refonte vers le NSMutableArray*, Vous lancez en fait un NSMutableArray**. Cela entraînera l'infâme EXC_BAD_ACCESS. Vous devez passer le pointeur lui-même, pas un pointeur vers un pointeur. Mais , CGPathApply(path, pathPoints, _processPathElement) ne fonctionnera pas, vous ne pouvez pas faire passer un NSMutableArray* Comme un void* . Ce dont vous avez besoin (ironiquement), c'est un pont. Pour les mêmes raisons que précédemment, il vous suffit de __bridge. Voir ci-dessous le code, avec les ponts corrects en place et fonctionnant comme prévu:

#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);
    }
}

Cela imprimera:

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

Je ne sais pas vraiment pourquoi cela fonctionne, mais j'ai trouvé la solution:

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

CGPathApply(path, (__bridge void*)pathPoints, _processPathElement);

Si quelqu'un peut expliquer pourquoi cela fonctionne et confirmer qu'il n'y a pas (ou pas) de fuite de mémoire, je vous serais reconnaissant

1
James Webster