9

众所周知,使用 ARC,我们需要 a__bridge将 id 转换为 a void *

void *t = (void *)self;           // ERROR: Cast of ... requires a bridged cast
void *t = (__bridge void *)self;  // CORRECT

C函数调用也是如此:

void f(void *t) {
  ....
}

f((void *)self);           // ERROR
f((__bridge void *)self);  // CORRECT

我认为这也应该适用于方法,实际上iOS 5教程中的这个Beginning ARC给出了以下示例,并说__bridge需要:

MyClass *myObject = [[MyClass alloc] init];
[UIView beginAnimations:nil context:(__bridge void *)myObject];

但是,今天我不小心在我__bridge的一个程序中的方法调用中删除了一个,并且代码编译并运行没有任何问题。上面示例中的__bridge似乎是不必要的:

[UIView beginAnimations:nil context:(void *)myObject];  // COMPILED OK

这是正确的吗?__bridge在这种情况下真的没有必要吗?或者删除它会改变代码的含义?

4

2 回答 2

6

这在ARC 文档第 3.3.3 节(重点是我的)中有所介绍:

3.3.3 在某些上下文中从可保留对象指针类型转换

[开始 Apple 4.0,LLVM 3.1]

如果可保留对象指针类型的表达式显式转换为 C 可保留指针类型,则程序是如上所述的格式错误,除非立即使用结果:

  • 在未使用 cf_consumed 属性标记参数的情况下初始化 Objective-C 消息发送中的参数,或
  • 在对未使用 cf_consumed 属性标记的参数的直接调用中初始化参数。

在您的代码中,myObject是一个“可保留对象指针”。一个“C 可保留指针类型”包括void*(这是一个稍微草率的定义,他们将其用作占位符,因为 Core Foundation 的“对象”通常是void*)。

所以一个 ObjC 对象可以隐式转换为一个void*if 用作方法参数。在这种情况下,没有额外的内存管理语义(即它相当于__bridge强制转换)。第 7.8 节警告我们,void*将来可能不会被这样对待,但我不会担心。如果发生这种情况,添加__bridge将是微不足道的。

要记住的一件事是myObject这里不受保护。在动画完成之前,您可以确保以其他方式保留它,否则您可能会崩溃。

于 2014-01-26T17:21:16.290 回答
2

__bridge用于传递变量/引用的所有权(如保留计数),即 C api 到 Objective-C 或 Objective-C 到 API。

浏览Clang 的文档

桥接演员表

桥接强制转换是使用以下三个关键字之一注释的 C 样式强制转换:

(__bridge T) op casts the operand to the destination type T. If T is a retainable object pointer type, then op must have a

不可保留的指针类型。如果 T 是不可保留的指针类型,则 op 必须具有可保留的对象指针类型。否则,演员阵容不正确。没有所有权转移,ARC 没有插入保留操作。(__bridge_retained T) op 将必须具有可保留对象指针类型的操作数强制转换为必须是不可保留指针类型的目标类型。ARC 保留该值,但需对本地值进行通常的优化,并且接收者负责平衡该 +1。(__bridge_transfer T) op 将必须具有不可保留指针类型的操作数强制转换为目标类型,该目标类型必须是可保留对象指针类型。ARC 将在封闭的完整表达式的末尾释放值,但要服从对局部值的通常优化。

需要这些转换才能将对象移入和移出 ARC 控制;请参阅关于可保留对象指针的转换部分的基本原理。

纯粹使用 __bridge_retained 或 __bridge_transfer 强制转换来说服 ARC 分别发出不平衡的保留或释放是不好的形式。

现在,

无效*t =(无效*)自我; // ERROR: Cast of ... requires a bridged cast 为什么会出错,因为您尝试将引用从 Objective-C 转换为 C。它未能传递引用的所有权。

void *t = (__bridge void *)self; // 正确为什么它正确,因为将您的目标 C 引用转移到 C。根据 LLVM 的文档。见上面给出的铸造规则。

MyClass *myObject = [[MyClass alloc] init];
[UIView beginAnimations:nil context:(__bridge void *)myObject];

上面几行完全没问题,因为您传递的是 C 引用类型上下文而不是NULL

于 2014-01-26T11:42:17.660 回答