ARC和桥接转换

172
使用ARC后,我无法再将CGColorRef强制转换为id。我了解到需要执行桥接强制转换。根据Clang文档的说明:
引用式强制转换是一种带有以下三个关键字之一的C样式强制转换:
  • (__bridge T) op将操作数转换为目标类型T。如果T是可保留的对象指针类型,则op必须具有不可保留的指针类型。如果T是不可保留的指针类型,则op必须具有可保留的对象指针类型。否则,该转换是非法的。没有所有权的转移,ARC不插入任何保留操作。
  • (__bridge_retained T) op将操作数(必须具有可保留的对象指针类型)转换为目标类型(必须是不可保留的指针类型)。ARC保留该值,但受本地值上的通常优化限制,并且接收方负责平衡+1。
  • (__bridge_transfer T) op将操作数(必须具有不可保留的指针类型)转换为目标类型(必须是可保留的对象指针类型)。ARC将在封闭完整表达式的末尾释放该值,但受本地值上的通常优化限制。
要在ARC控制中输入和输出对象时,需要使用这些强制转换;请参见关于可保留对象指针转换的原理的部分的基本原理。纯粹使用__bridge_retained__bridge_transfer强制转换来说服ARC分别发出不平衡的保留或释放是不恰当的。
应在哪些情况下使用每个强制转换?
例如,CAGradientLayer具有接受CGColorRef数组的colors属性。我猜应该在这里使用__bridge,但为什么应该使用(或不使用)不清楚。

17
你看过WWDC 2011的323场会议吗?那个比我在这里解释得更好。它从头到尾讲解了ARC的所有细节。这是每个Mac/iOS开发者必须观看的会议。 - rbrown
这可能也有帮助:https://dev59.com/3mYq5IYBdhLWcg3wmRua#28495303 - Ewan Mellor
WWDC会议的链接,找起来并不容易:developer.apple.com/videos/play/wwdc2011/323 - 相关部分在23:15。 - Daniel
3个回答

221

我同意这段描述有些令人困惑。由于我刚刚理解了它们,我会尝试总结一下:

  • (__bridge_transfer <NSType>) op 或者 CFBridgingRelease(op) 用于在转移到ARC时消耗一个 CFTypeRef 的保留计数。这也可以表示为 id someObj = (__bridge <NSType>) op; CFRelease(op);

  • (__bridge_retained <CFType>) op 或者 CFBridgingRetain(op) 用于将 NSObject 移交到 CF-land,同时给其+1个保留计数。您应该像处理 CFStringCreateCopy() 的结果一样处理使用此方法创建的 CFTypeRef。这也可以表示为 CFRetain((__bridge CFType)op); CFTypeRef someTypeRef = (__bridge CFType)op;

  • __bridge 只是在指针区和Objective-C对象区之间进行类型转换。如果您没有倾向于使用上面的转换,请使用此方法。

也许这很有帮助。我自己相当喜欢使用 CFBridging… 宏而不是普通的类型转换。


当您使用__bridge_transfer时,对象的保留计数是否会增加1个ARC?否则,似乎在调用CFRelease()时,对象已经消失并指向空。同样,当您使用__bridge_retain时,ARC是否将op的保留计数减少1个?否则,该对象似乎永远不会被正确释放。 - Tony
2
在 ARC(自动引用计数) 的世界里,你不再需要考虑保留计数,只需考虑强引用和弱引用。 - monkeydom
4
如果你只在 ARC 环境下开发,那么只要了解 strong 和 weak 就足够了。但是当你需要在 ARC 和非 ARC 环境中相互转换对象时,你仍然需要考虑底层的引用计数影响。 - Tony
3
不完全是。你只需考虑如何进入和离开ARC环境。这与掌握自动释放内存有些相似。(有趣的是:ARC修复了一些常见模式,例如从字典中获取对象,然后在使用之前将其删除等。) - monkeydom
3
使用分析器工具(Shift + Command + B)可以帮助解决此类疑问,因为它会用自然语言告诉您当前的代码是否存在内存泄漏问题。如果有内存泄漏问题,则可能使用了保留转换而应该使用非保留转换。 如果分析器在这些代码行中没有发出警告,则当前的代码可能运行良好。 - Fabio Napodano

57

我在iOS文档中找到了另一个解释,认为这更容易理解:

  • __bridge 在Objective-C和Core Foundation之间传递指针而不转移所有权。

  • __bridge_retained (CFBridgingRetain)Objective-C指针强制转换为Core Foundation指针,并将所有权转移到你。

    您负责调用CFRelease或相关函数来放弃对象的所有权。

  • __bridge_transfer (CFBridgingRelease)非Objective-C指针移动到Objective-C,并将所有权转移到ARC。

    ARC负责放弃对象的所有权。

来源:Toll-Free Bridged Types


33
作为后续,对于这种情况,如果您使用的是iOS系统,苹果建议使用UIColor及其-CGColor方法将CGColorRef返回到colors NSArray中。在过渡到ARC发布说明中,在“编译器处理从Cocoa方法返回的CF对象”一节中,指出使用像-CGColor这样返回Core Foundation对象的方法将由编译器自动正确处理。
因此,他们建议使用以下代码:
CAGradientLayer *gradientLayer = (CAGradientLayer *)[self layer];
gradientLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor darkGrayColor] CGColor],
                                                 (id)[[UIColor lightGrayColor] CGColor], nil];

注意,截至目前,苹果的示例代码缺少我上面提到的(id)转换,这仍然是必要的,以避免编译器错误。

通常情况下,如果你愿意的话,你只需要将对象中的第一个进行强制类型转换为(id),而不是全部都转换。 - Philippe Sabourin
1
这个问题涉及到使用ARC进行类型转换,而你粘贴的代码是不合法的。 - Joey Hagedorn
11
也许你错过了我在回答中第一句话提到的ARC文档的引用,但不仅在ARC下这是有效的,而且这也是从这些UIColor转换方法提供CGColorRef引用的推荐方法。我和许多其他人在启用ARC的应用程序中都使用这个精确的代码。从返回一个核心基础对象的方法立即强制转换为(id)可以自动正确地将该对象桥接到ARC。 - Brad Larson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接