方法参数不需要__bridge?
Posted
技术标签:
【中文标题】方法参数不需要__bridge?【英文标题】:__bridge not needed for method arguments? 【发布时间】:2014-02-17 03:46:27 【问题描述】:我们知道,使用 ARC,我们需要 __bridge
来将 id 转换为 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
我认为这也应该适用于方法,实际上这个Beginning ARC in ios 5 Tutorial 给出了以下示例,并说需要__bridge
:
MyClass *myObject = [[MyClass alloc] init];
[UIView beginAnimations:nil context:(__bridge void *)myObject];
但是,今天我不小心在我的一个程序的方法调用中删除了__bridge
,并且代码编译并运行没有任何问题。上面示例中的__bridge
似乎是不必要的:
[UIView beginAnimations:nil context:(void *)myObject]; // COMPILED OK
这是对的吗?在这种情况下,__bridge
真的不需要吗?或者删除它会改变代码的含义?
【问题讨论】:
【参考方案1】:ARC docs section 3.3.3(强调我的)对此进行了介绍:
3.3.3 在某些上下文中从可保留对象指针类型转换
[开始 Apple 4.0,LLVM 3.1]
如果可保留对象指针类型的表达式被显式转换 对于 C 可保留指针类型,程序是不正确的,如所讨论的 除非立即使用结果:
初始化 Objective-C 消息中的参数 参数没有用 cf_consumed 属性标记,或者 在直接调用审计函数时初始化参数,其中 该参数未使用 cf_consumed 属性进行标记。
在您的代码中,myObject
是“可保留对象指针”。 “C 可保留指针类型”包括 void*
(这是一个稍微草率的定义,他们将其用作占位符,因为核心基础“对象”通常是 void*
)。
因此,如果将 ObjC 对象用作方法参数,则可以将其隐式转换为 void*
。在这种情况下,没有额外的内存管理语义(即它相当于__bridge
演员表)。 Section 7.8 警告我们 void*
将来可能不会被这样对待,但我不会担心。如果发生这种情况,添加__bridge
将是微不足道的。
要记住的一件事是myObject
在这里不受保护。您可以确保以其他方式保留它直到动画完成,否则您可能会崩溃。
【讨论】:
非常有趣,而且很有发现。 +1 因为你给了我另一个讨厌 ARC 的理由 ;-)【参考方案2】:__bridge
用于传递变量/引用的所有权(如保留计数),即 C api 到 Objective-C 或 Objective-C 到 API。
通过Clang's doc:
桥接演员表
桥接转换是用三个关键字之一注释的 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 会在最后释放值 封闭的完整表达式,服从通常的优化 当地价值观。
需要这些转换才能将对象移入和移出 电弧控制;参见关于转换的部分的基本原理 可保留的对象指针。
纯粹使用 __bridge_retained 或 __bridge_transfer 强制转换来说服 ARC分别发出不平衡的保留或释放很差 表格。
现在,
void *t = (void *)self; // 错误:... 的演员表需要桥接演员表 为什么会出错,因为您尝试将引用从 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
【讨论】:
以上是关于方法参数不需要__bridge?的主要内容,如果未能解决你的问题,请参考以下文章