什么是 Toll-Free Bridging

有一些数据类型是能够在 Core Foundation Framework(Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能) 和 Foundation Framework 之间交换使用的。这意味着,对于同一个数据类型,你既可以将其作为参数传入 Core Foundation 函数,也可以将其作为接收者对其发送 Objective-C 消息(即调用ObjC类方法)。这种在 Core Foundation 和 Foundation 之间交换使用数据类型的技术就叫 Toll-Free Bridging.(所谓的Toll-free bridging是说您可以在某个框架的方法或函数同时使用Core Foundatio和Foundation 框架中的某些类型。很多数据类型支持这一特性,其中包括群体和字符串数据类型。每个框架的类和类型描述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。)

举例说明,NSString和CFStringRef即是一对可以相互转换的数据类型:

  1. // ARC 环境下
  2. // Bridging from ObjC to CF
  3. NSString *hello = @"world";
  4. CFStringRef world = (__bridge CFStringRef)(hello);
  5. NSLog(@"%ld", CFStringGetLength(world));
  6. // Bridging from CF to ObjC
  7. CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
  8. NSString *world = (__bridge NSString *)(hello);
  9. NSLog(@"%ld", world.length);
  10. CFRelease(hello);

大部分(但不是所有!)Core Foundation 和 Foundation 的数据类型可以使用这个技术相互转换,Apple 的文档里有一个列表(传送门),列出了支持这项技术的数据类型。

MRC 下的 Toll-Free Bridging 因为不涉及内存管理的转移,可以直接相互 bridge 而不必使用类似__bridge修饰字,我们之后再讨论这个问题。

Toll-Free Bridging 是如何实现的?

1. 每一个能够 bridge 的 ObjC 类,都是一个类簇(class cluster)。类簇是一个公开的抽象类,但其核心功能的是在不同的私有子类中实现的,公开类只暴露一致的接口和实现一些辅助的创建方法。而与该 ObjC 类相对应的 Core Foundation 类的内存结构,正好与类簇的其中一个私有子类相同。

举个例子,NSString是一个类簇,一个公开的抽象类,但每次创建一个NSString的实例时,实际上我们会获得其中一个私有子类的实例。而NSString的其中一个私有子类实现既为NSCFString,其内存的结构与CFString是相同的,CFString的isa指针就指向NSCFString类,即,CFString对象就是一个NSCFString类的实例。

所以,当NSString的实现刚好是NSCFString的时候,他们两者之间的转换是相当容易而直接的,他们就是同一个类的实例。

2. 当NSString的实现不是NSCFString的时候(比如我们自己 subclass 了NSString),我们调用 CF 函数,就需要先检查对象的具体实现。如果发现其不是NSCFString,我们不会调用 CF 函数的实现来获得结果,而是通过给对象发送与函数功能相对应的 ObjC 消息(调用相对应的NSString的接口)来获得其结果。

例如CFStringGetLength函数,当收到一个作为参数传递进来的对象时,会先确认该对象到底是不是NSCFString实现。如果是的话,就会直接调用CFStringGetLength函数的实现来获得字符串的长度;如果不是的话,会给对象发送length消息(调用NSString的- (NSUInteger)length接口),来得到字符串的长度。

通过这样的技术,即使是我们自己子类了一个NSString,也可以和CFStringRef相互 Bridge。

3. 其他支持 Toll-Free Bridging 的数据类型原理也同NSString一样,比如NSNumber的NSCFNumber和CFNumber。

ARC 下的 Toll-Free Bridging

如之前提到的,MRC 下的 Toll-Free Bridging 因为不涉及内存管理的转移,相互之间可以直接交换使用:

  1. // bridge
  2. NSString *nsStr = (NSString *)cfStr;
  3. CFStringRef cfStr = (CFStringRef)nsStr;
  4. // 调用函数或者方法
  5. NSUInteger length = [(NSString *)cfStr length];
  6. NSUInteger length = CFStringGetLength((CFStringRef)nsStr);
  7. // release
  8. CFRelease((CFStringRef)nsStr);
  9. [(NSString *)cfStr release];

而在 ARC 下,事情就会变得复杂一些,因为 ARC 能够管理 Objective-C 对象的内存,却不能管理 CF 对象,CF 对象依然需要我们手动管理内存。在 CF 和 ObjC 之间 bridge 对象的时候,问题就出现了,编译器不知道该如何处理这个同时有 ObjC 指针和 CFTypeRef 指向的对象。

这时候,我们需要使用__bridge, __bridge_retained, __bridge_transfer 修饰符来告诉编译器该如何去做。

__bridge

最常用的修饰符,这意味着告诉编译器不做任何内存管理的事情,编译器仍然负责管理好在 Objc 一端的引用计数的事情,开发者也继续负责管理好在 CF 一端的事情。举例说明:

例子1

  1. // objc to cf
  2. NSString *nsStr = [self createSomeNSString];
  3. CFStringRef cfStr = (__bridge CFStringRef)nsStr;
  4. CFUseCFString(cfStr);
  5. // CFRelease(cfStr); 不需要

在这里,编译器会继续负责nsStr的内存管理的事情,不会在 bridge 的时候 retain 对象,所以也不需要开发者在 CF 一端释放。需要注意的是,当nsStr被释放的时候(比如出了作用域),意味着cfStr指向的对象被释放了,这时如果继续使用cfStr将会引起程序崩溃。

例子2

  1. // cf to objc
  2. CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
  3. NSString *world = (__bridge NSString *)(hello);
  4. CFRelease(hello); // 需要
  5. [self useNSString:world];

在这里,bridge 的时候编译器不会做任何内存管理的事情,bridge 之后,会负责 ObjC 一端的内存管理的事情 。同时,开发者需要负责管理 CF 一端的内存管理的事情,需要再 bridge 之后,负责 release 对象。

__bridge_retained

接__bridge一节的第一个例子,objc to cf。为了防止nsStr被释放,引起我们使用cfStr的时候程序崩溃,可以使用__bridge_retained修饰符。这意味着,在 bridge 的时候,编译器会 retain 对象,而由开发者在 CF 一端负责 release。这样,就算nsStr在 objc 一端被释放,只要开发者不手动去释放cfStr,其指向的对象就不会被真的销毁。但同时,开发者也必须保证和负责对象的释放。例如:

  1. // objc to cf
  2. NSString *nsStr = [self createSomeNSString];
  3. CFStringRef cfStr = (__bridge_retained CFStringRef)nsStr;
  4. CFUseCFString(cfStr);
  5. CFRelease(cfStr); // 需要

__bridge_transfer

接__bridge一节的第二个例子,cf to objc。我们发现如果使用__bridge修饰符在cf转objc的时候非常的麻烦,我们既需要一个CFTypeRef的变量,还需要在 bridge 之后负责释放。这时我们可以使用__bridge_transfer,意味着在 bridge 的时候,编译器转移了对象的所有权,开发者不再需要负责对象的释放。例如:

  1. // cf to objc
  2. CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
  3. NSString *world = (__bridge_transfer NSString *)(hello);
  4. // CFRelease(hello); 不需要
  5. [self useNSString:world];

甚至可以这么写:

  1. // cf to objc
  2. NSString *world = (__bridge_transfer NSString *)CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
  3. [self useNSString:world];

小结

(__bridge T) op:告诉编译器在 bridge 的时候不要做任何事情

(__bridge_retained T) op:( ObjC 转 CF 的时候使用)告诉编译器在 bridge 的时候 retain 对象,开发者需要在CF一端负责释放对象

(__bridge_transfer T) op:( CF 转 ObjC 的时候使用)告诉编译器转移 CF 对象的所有权,开发者不再需要在CF一端负责释放对象

参考

Concepts in Objective-C Programming

Core Foundation Design Concepts

Toll Free Bridging Internals

Clang documentation: Objective-C Automatic Reference Counting (ARC)

转载自:Lancy's Blog

什么是 Toll-Free Bridging相关推荐

  1. ios5 ARC机制介绍和使用

    参考http://www.yifeiyang.net/development-of-the-iphone-simply-1/ http://blog.csdn.net/diyagoanyhacker/ ...

  2. 理解ARC在Objective-C中的应用

    原文:http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/ 名词解 ...

  3. CFTimeInterval 和 NSTimeInterval 的区别

    在网上搜很多关于这两个的区别所在--最后得到的结论是----没有区别,除了名字不同. 官方称这种为"Toll Free Bridging". 在NS和CF之间存在很多相同和类似.

  4. Bridging signals(二分 二分+stl dp)

    欢迎参加--每周六晚的BestCoder(有米!) Bridging signals Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 6 ...

  5. Cow Toll Paths(floyd变形)

    链接:https://ac.nowcoder.com/acm/contest/1077/K 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言6553 ...

  6. Bridging signals(最长上升子序列)

    Bridging signals 题目 "哦,不,他们又做到了",Waferland 芯片厂的首席设计师喊道.布线设计人员再次完全搞砸了,使连接两个功能块端口的芯片上的信号到处都是 ...

  7. Bridging the Gap between Training and Inference for Neural Machine Translation翻译

    Bridging the Gap between Training and Inference for Neural Machine Translation 翻译 原文链接:https://arxiv ...

  8. Deliberated Domain Bridging for Domain Adaptive Semantic Segmentation

    Deliberated Domain Bridging for Domain Adaptive Semantic Segmentation Abstract   许多UDA的工作就是尝试通过各种中间空 ...

  9. 论文《DeepHawkes: Bridging the Gap between Prediction and Understanding of Information Cascades》阅读

    论文<DeepHawkes: Bridging the Gap between Prediction and Understanding of Information Cascades>阅 ...

最新文章

  1. 打破欧美垄断,国防科大斩获“航天界奥林匹克”大赛首冠
  2. 搭建hadoop2.6.0 HA及YARN HA
  3. 如何判断一个整数数组中是否有重复元素?要求时间复杂度O(n),空间复杂度O(1)
  4. linux中的umask命令
  5. 用Eclipse进行C/C++开发
  6. jersey put 服务_项目学生:带有Jersey的Web服务服务器
  7. 【水】uva10037 过桥
  8. iterm2 agnoster主题设置中的一些踩坑 2018.8
  9. 魔术师利用一副牌中的13张红桃c语言,魔术师的猜牌术(1) 魔术师利用一副牌中的13张黑桃 - 下载 - 搜珍网...
  10. script标签async和defer的区别及作用
  11. 一篇关于Dataset与泛型、自定义集合的讨论
  12. 《机械基础》记忆性内容总结
  13. 如何卸载 Adobe Creative Cloud 桌面应用程序
  14. php代码的网页框架,easyweb: easyweb是一个简单易用的php框架,实现了路由、容错等处理,集成composer代码生态,可快速创建中小型网站应用...
  15. python共享单车数据分析_共享单车数据可视化分析(Python/Seaborn)
  16. Linux中如何设置静态IP和动态ip设定
  17. GPS 入门 5 —— 定位误差产生的原因和差分定位原理 (转)
  18. cocos creator 加载微信云端图片
  19. [渝粤教育] 北京化工大学 自然辩证法概论 参考 资料
  20. EFR32 gecko 2生产烧录

热门文章

  1. AW codec驱动跨平台移植
  2. 大学物理第一章 质点运动学详解
  3. x²-dy²=-1有多少整数解?近30年无人解开的数学难题有答案了
  4. 爆肝!!! orcale 期末复习资料整理
  5. 海思平台ISP调试经验
  6. 敏捷领导力系列介绍--全景领导力LCP的学习心得
  7. 破解flashFXP密码全过程
  8. logit方程怎么写_呆哥数学坐标系与参数方程——最全概括【1】
  9. 中文乱码与字体库windows
  10. C语言实现过滤ASCII在0~127范围内的字符,并去除重复的字符