40

我需要一些关于__bridgeiOS 中的 -ing 的建议。

希望下面的 SSCCE 1能比我用文字更好地解释这个问题,但我需要知道如何将 a 转换void*NSMutableArray*; __bridge应该使用哪种变体(参见代码中的注释)。

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

最终,我希望CGPointsCGPathafterCGPathApply被调用后有一个数组。

#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

4

2 回答 2

68

可以在此处找到有关使用 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 方法控制其内存管理,并且只是将引用传递给它调用的方法(尽管是间接的,但都在 main 的范围内)。因此,您将使用__bridge.

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

啊,这是您发布的代码的问题,与整个桥接讨论无关。您需要将 a 传递void*给 CGApplyPath,用于您的处理功能_processPathElement。你正在传递的是NSMutableArray**.

当你重铸到 时NSMutableArray*,你实际上是在投射NSMutableArray**. 这将导致臭名昭著的 EXC_BAD_ACCESS。您需要传递指针本身,而不是指向指针的指针。但是,CGPathApply(path, pathPoints, _processPathElement)行不通,你不能把 aNSMutableArray*当作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);
    }
}

这将打印出:

Points:(
    "NSPoint: {0, 0}",
    "NSPoint: {1, 0}",
    "NSPoint: {1, 1}",
    "NSPoint: {0, 1}"
)
于 2013-02-13T14:28:46.413 回答
1

我实际上不确定为什么会这样,但我发现解决方案是:

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

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

如果有人能解释为什么这有效并确认没有(/是)任何内存泄漏,我将不胜感激

于 2013-02-13T14:19:21.917 回答