5

我只是CABasicAnimation为 shadowPath 配置了一个属性,这让我很好奇:

shadowAnimation.toValue = (id)newShadowPath.CGPath;

[注:shadowAnimation 是一个CABasicAnimation对象,newShadowPath是一个UIBezierPath对象]

这行得通,并且在 Xcode 中看不到任何错误/警告。但是,如果我这样写:

CGPathRef test = newShadowPath.CGPath;
shadowAnimation.toValue = (id)test;

这不会编译,抛出这个警告信息:

Cast of C pointer type 'CGPathRef' (aka 'const struct CGPath *') to Objective-C pointer type 'id' requires a bridged cast

所以它需要我这样输入:

shadowAnimation.toValue = (__bridge id)test;

为什么会这样?当我仅使用(id)newShadowPath.CGPath时,为什么在初始示例中没有出现相同的错误;? __bridge不管 Xcode 没有检测到任何问题,将 cast 也放在那里是否正确?还是我错过了这里的区别?

4

2 回答 2

2

这是由 ARC 引起的,以及它在分配发生时的魔力。

当您创建一个临时变量CGPathRef test并将其分配给它时,ARC 会看到您正在使用一个 CoreBlank 实例,该实例不遵循标准内存范例并且不保留它或做任何花哨的事情。

后来,当你尝试将这个非托管实例分配给一个保留类型的id属性时,LLVM 会吓坏,因为它认为它不必为那个实例管理内存,但现在它应该开始(这是__bridge关键字的意图)。

在第一个代码片段中,它起作用的原因是因为 ARC 总是知道它应该将该实例视为 ARC 管理的,并且永远不存在对它的引用而不是 ARC 管理的。

于 2013-03-07T11:06:28.693 回答
2

造成差异的原因在于 clang 3.1 中引入的新的(并且有点复杂的)转换规则。

首先,让我们把它归结为:

@@interface Foo : NSObject
+ (CGPathRef)bar;
@end

CGPathRef foo();

void test()
{
    // works without bridged cast:
    id a = (id)[Foo bar];

    // needs bridged cast
    id b = (__bridge id)foo();
}

因此,当转换消息的结果时,我们可以省略 brdge 转换,而普通函数调用需要它。这似乎很奇怪。

差异的原因在于 ARC 如何解释函数和方法名称,并得出关于所谓的C 可保留指针类型(核心基础对象)的保留计数的假设。

在ARC 指南的第 3.3.2 节(“转换为具有已知语义的表达式的可保留对象指针类型”)中,您会找到差异的原因:

如果表达式是C 可保留指针类型的右值并且它是 [...] 消息发送 [...]

如果已知转换操作数未保留[...],则转换被视为 __bridge 转换

同一部分描述了为什么这不适用于 C 函数以及如何修饰它们以使 clang 能够做出类似的假设。所以我们可以修改上面的例子来去掉 C 函数调用的桥接:

CGPathRef foo() __attribute__((cf_returns_not_retained));

回答您的最后一个问题:在两个地方都使用桥架是安全的。它甚至确保 ARC 选择正确的__bridge演员表(它可以__bridge_transfer根据方法的名称来选择演员表。在这种情况下,它会使用__bridge,不过)。

于 2013-03-07T12:12:32.053 回答