__bridge的位置和方式

Jam*_*ter 33 memory-management core-graphics objective-c cgpath ios

我需要__bridge在iOS 上提供一些建议.

希望在SSCCE 1下面会解释这个问题比我的话比较好,但我需要知道我可以转换void*NSMutableArray*; __bridge应该使用哪种变体(参见代码中的注释).

阅读不同的桥梁,我推断出我需要,__bridge_transfer但后来我收到了一个EXC_BAD_ACCESSaddObject:

最终,我想CGPointsCGPath之后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);
    }
}
Run Code Online (Sandbox Code Playgroud)

1:SSCCE

WDU*_*DUK 60

有关使用bridge关键字的文档可以在这里找到.具体来说,我想指出§3.2.4:

(__bridge T) op将操作数强制转换为目标类型T.如果T是可保留的对象指针类型,则op必须具有不可保留的指针类型.如果T是不可保留的指针类型,则op必须具有可保留的对象指针类型.否则演员阵容不合理.没有所有权转让,ARC不会保留任何保留操作.

(__bridge_retained T) op将必须具有可保留对象指针类型的操作数强制转换为目标类型,该目标类型必须是不可保留的指针类型.ARC保留该值,取决于对本地值的通常优化,并且接收方负责平衡+1.

(__bridge_transfer T) op将操作数(必须具有不可保留的指针类型)强制转换为目标类型,该目标类型必须是可保留的对象指针类型.ARC将在封闭的完整表达式的末尾释放值,这取决于对本地值的通常优化.

您传入的指针(void*)是不可保留的指针类型,而您的NSMutableArray是可保留的指针类型.这__bridge_retained直接排除了.所以问题是,是__bridge或是__bridge_transfer

__bridge_transfer当您需要来自返回已保留的CF对象的方法的Objective-C指针时,通常会使用此方法.例如,CFStringCreateWithFormat将返回一个保留的CFString,但是如果你想要一个NSString,你需要__bridge_transfer它们之间.这将使ARC在适当时释放CF保留的对象.例如,NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

您的代码没有这样做,您不需要干涉所有权.您的主要方法是控制其内存管理,并且只是将引用传递给它调用的方法(尽管是间接的,但它都在main的范围内).因此,你会使用__bridge.

但等等,当我使用__bridge时,我的代码会出现内存访问错误!?

啊,这是您发布的代码的问题,与整个桥接讨论无关.您需要传递void*给CGApplyPath,以获得处理功能_processPathElement.你传递的是什么NSMutableArray**.

当你重铸时NSMutableArray*,你实际上正在施展NSMutableArray**.这将导致臭名昭着的EXC_BAD_ACCESS.您需要传递指针本身,而不是指向指针的指针.但是,CGPathApply(path, pathPoints, _processPathElement)不会起作用,你不能把它NSMutableArray*当成一个void*.你需要什么(具有讽刺意味),是一座桥梁.出于与以前相同的原因,您只需要__bridge.请参阅下面的代码,使用正确的桥接器,并按预期工作:

#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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将打印出来:

Points:(
    "NSPoint: {0, 0}",
    "NSPoint: {1, 0}",
    "NSPoint: {1, 1}",
    "NSPoint: {0, 1}"
)
Run Code Online (Sandbox Code Playgroud)