ios Toll-Free Bridging

时间:2022-09-01 13:05:27

有一些数据类型是能够在 Core Foundation Framework 和 Foundation Framework 之间交换使用的。这意味着,对于同一个数据类型,你既可以将其作为参数传入 Core Foundation 函数,也可以将其作为接收者对其发送 Objective-C 消息(即调用ObjC类方法)。这种在 Core Foundation 和 Foundation 之间交换使用数据类型的技术就叫 Toll-Free Bridging.

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

// ARC 环境下
// Bridging from ObjC to CF
NSString *hello = @"world";
CFStringRef world = (__bridge CFStringRef)(hello);
NSLog(@"%ld", CFStringGetLength(world)); // Bridging from CF to ObjC
CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
NSString *world = (__bridge NSString *)(hello);
NSLog(@"%ld", world.length);
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 因为不涉及内存管理的转移,相互之间可以直接交换使用:

// bridge
NSString *nsStr = (NSString *)cfStr;
CFStringRef cfStr = (CFStringRef)nsStr; // 调用函数或者方法
NSUInteger length = [(NSString *)cfStr length];
NSUInteger length = CFStringGetLength((CFStringRef)nsStr); // release
CFRelease((CFStringRef)nsStr);
[(NSString *)cfStr release];

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

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

__bridge

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

例子1

// objc to cf
NSString *nsStr = [self createSomeNSString];
CFStringRef cfStr = (__bridge CFStringRef)nsStr;
CFUseCFString(cfStr);
// CFRelease(cfStr); 不需要

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

例子2

// cf to objc
CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
NSString *world = (__bridge NSString *)(hello);
CFRelease(hello); // 需要
[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,其指向的对象就不会被真的销毁。但同时,开发者也必须保证和负责对象的释放。例如:

// objc to cf
NSString *nsStr = [self createSomeNSString];
CFStringRef cfStr = (__bridge_retained CFStringRef)nsStr;
CFUseCFString(cfStr);
CFRelease(cfStr); // 需要

__bridge_transfer

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

// cf to objc
CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
NSString *world = (__bridge_transfer NSString *)(hello);
// CFRelease(hello); 不需要
[self useNSString:world];
// cf to objc
NSString *world = (__bridge_transfer NSString *)CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
[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)

转至:http://gracelancy.com/blog/2014/04/21/toll-free-bridging/

ios Toll-Free Bridging的更多相关文章

  1. 理解 Objective-C 的 ARC

    英文原文:Understanding Automatic Reference Counting in Objective-C 自动引用计数(Automatic Reference Counting, ...

  2. xcode 手动管理内存 的相关知识点总结

    一.XCode4.2以后支持自动释放内存ARC xcode自4.2以后就支持自动释放内存了,但有时我们还是想手动管理内存,这如何处理呢. 很简单,想要取消自动释放,只要在  Build Setting ...

  3. iOS开发系列--Swift进阶

    概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...

  4. IOS 整体架构 和 MVC布局

    IOS的生态系统 IOS生态系统不仅仅是指产品,更重要的是指 iPhone/iPad/iPod/Mac +iCloud+App整个系统,包括Siri (部分设备不支持).FaceTime.Safari ...

  5. iOS 底层框架的浅析

    1.简介 IOS是由苹果公司为iPhone.iPod touch和iPad等设备开发的操作系统. 2.知识点 iPhone OS(现在叫iOS)是iPhone, iPod touch 和 iPad 设 ...

  6. IOS框架和服务

    在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像.声音文件等其他资源.共享资源库定义应用程序可以调用的函数和方法. iOS为应用程序开发提供了许多可使用的框架 ...

  7. iOS 动画

    图层树.寄宿图以及图层几何学(一)图层的树状结构 技术交流新QQ群:414971585 巨妖有图层,洋葱也有图层,你有吗?我们都有图层 -- 史莱克 Core Animation其实是一个令人误解的命 ...

  8. iOS开发ARC内存管理技术要点

    本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...

  9. iOS 动画学习

    图层树.寄宿图以及图层几何学(一)图层的树状结构 技术交流新QQ群:414971585 巨妖有图层,洋葱也有图层,你有吗?我们都有图层 -- 史莱克 Core Animation其实是一个令人误解的命 ...

随机推荐

  1. ABP框架 - 介绍

    文档目录 本节内容: 简介 一个快速示例 其它特性 启动模板 如何使用 简介 我们总是对不同的需求开发不同的应用.但至少在某些层面上,一次又一次地重复实现通用的和类似的功能.如:授权,验证,异常处理, ...

  2. 【003:switch 不加 break的结果

    #include <stdio.h> int main(){ char ch = 's'; switch(ch){ case 'a':{ printf("aaaaa") ...

  3. RDLC隔行变色的实现

    在RDLC设计页面设置行属性中的Background为: =IIF((RowNumber(Nothing)) Mod 2 =1,"Transparent","#F6FAF ...

  4. Java for LeetCode 205 Isomorphic Strings

    Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...

  5. Nodejs学习笔记(七)--- Node&period;js &plus; Express 构建网站简单示例

    目录 前言 新建项目.建立数据库以及其它准备工作 新建express + ejs 项目:sampleEjs 创建数据库 修改package.json文件,安装session和mysql模块 样式和JQ ...

  6. hibernate提供的5种检索数据方式

    一.五种检索数据方式 1.OID检索,即使用session.get或session.load通过类及指定id查询数据,如Customer c=(Customer)session.get("C ...

  7. Best practice&colon; escape&comma; or encodeURI &sol; encodeURIComponent

    escape() Don't use it, as it has been deprecated since ECMAScript v3. encodeURI() Use encodeURI when ...

  8. java对redis的基本操作&lpar;转&rpar;

    本文转自:http://www.cnblogs.com/edisonfeng/p/3571870.html 2.主要类 1)功能类 package com.redis; import java.uti ...

  9. Less 原理

    Less 原理 Less 包含一套自定义的语法及一个解析器,用户根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 CSS 文件. Less 并没有裁剪 CSS 原有的特性,更不 ...

  10. Eclipse 个人使用配置

    个人最喜欢使用的是eclipse,但是每次有新的版本或者是在不同的电脑上都要一遍一遍的配置.下面收集自己每次用eclipse需要注意配置的地方: 快捷键只需要修改一个code assitant 修改显 ...