在哪里以及如何__bridge

问题描述 投票:0回答:2

我需要一些关于 iOS 中

__bridge
-ing 的建议。

希望下面的示例能够比我用语言更好地解释问题,但我需要知道如何将

void*
转换为
NSMutableArray*
(即应该使用哪种
__bridge
变体)。

阅读有关不同桥梁的信息,我推断出我需要

__bridge_transfer
但随后我在
addObject:

上收到 EXC_BAD_ACCESS

最终,我希望在调用

CGPoints
后,在
CGPath
中有一个
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);
    }
}
ios objective-c memory-management
2个回答
76
投票

有关使用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

当您希望从返回已保留的 CF 对象的方法中获取 Objective-C 指针时,通常会使用

__bridge_transfer
。例如,CFStringCreateWithFormat 将返回一个保留的 CFString,但如果您想要从中获取 NSString,则需要在它们之间添加
__bridge_transfer
。这将使 ARC 在适当的时候释放 CF 保留的对象。例如,
NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

您的代码没有这样做,您不需要干涉所有权。您的 main 方法控制其内存管理,并且只是传递对其调用的方法的引用(尽管是间接的,但都在 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);
    }
}

这将打印出:

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

1
投票

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

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

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

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

© www.soinside.com 2019 - 2024. All rights reserved.