| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Swift 泛型底层实现原理 -> 正文阅读 |
|
[移动开发]Swift 泛型底层实现原理 |
协议类型在内存中的存储形式是 Extential Container,Extential Container 占 5 个内存单元(也称 词),其作用如下:
泛型类型由于在调用时能够确定具体的类型,所以不需要使用 Extential Container。在调用泛型方法时,只需要将 Value Witness Table/Protocol Witness Table 作为额外参数进行传递。 内存管理泛型类型使用 VWT 进行内存管理,VWT 由编译器生成,其存储了该类型的 size、aligment(对齐方式)以及针对该类型的基本内存操作。其结构如下所示(以 C 代码表示):
当对泛型类型进行内存操作(如:内存拷贝)时,最终会调用对应泛型类型的 VWT 中的基本内存操作。泛型类型不同,其对应的 VWT 也不同。下图所示为一个小的值类型和一个引用类型的 VWT。
方法调用上一节,我们介绍了泛型的内存管理。那么,泛型的方法调用又是如何实现的呢? 我们以如下一个泛型函数为例进行介绍。
编译器对上述的泛型函数进行编译后,会得到如下代码(以 C 代码代替 LLVM IR)。
从生成的代码中可以看出,方法运行时会传入一个 *type T。很明显,这是一个类型参数,描述泛型类型所绑定的具体类型的元信息,包括对 VWT 的索引信息。 f 是一个对泛型类型进行拷贝的方法,囊括了上述介绍的内存管理的过程,下面我们来简要分析其编译后的源码:
上述泛型函数的实现中,*type T 是整个函数能够顺利运行的关键。那么 *type T 到底是什么呢? Type Metadata事实上,编译器在会尽量在编译时为每一个类型生成一个类型元信息对象——Type Metadata,也就是上述的 type *T。 Type Metadata 携带的类型元信息主要包含:类型的 VWT、类型的反射信息。如下图所示: 因此对于不使用 Extential Container 进行表示的泛型类型来说,通过 Type Metadata 也可以索引到 VWT。
我们再来看 OOP 是如何通过 virtual table 来实现动态派发的。如下图所示
对于自定义的引用类型,Type Metadata 会在我们的程序中生成,VWT 则由所有引用类型共享,即上述针对引用类型的标准 VWT。 下面,我们再以上述 f 函数的具体调用来进行介绍编译后的代码是如何使用 Type Metadata 的。如下所示为两种类型对 f 的调用。
当使用 int 类型和 MyStruct 类型调用 f 时,编译器生成的代码如下所示:
两者的区别在于:
为了了解 Type Metadata 的动态生成,我们需要先了解 Metadata Pattern。 Metadata Pattern事实上,对于泛型类型,编译器会在编译时生成一个 Metadata Pattern。Metadata Pattern 与 Type Metadata 的关系其实就是类与对象的关系。 以如下自定义泛型类结构为例。
对于运行时才能确定的泛型类型,运行时根据绑定类型的 Type Metadata,结合 Metadata Pattern,生成最终的确定类型的 Type Metadata。如下图所示:
下面,我们通过一个泛型属性访问的例子来看看运行时是如何使用 Metadata Pattern 来生成 Type Metadata。以如下代码为例:
编译器生成的代码如下:
其步骤如下:
编译优化上述代码是在编译时无法推断出泛型类型的情况下生成的通用型代码。然而,如果在编译时就能推导出泛型类型,编译器则会进行优化,在真正运行时避免通过传递 Type Metadata 来查找各个域的偏移,从而提高运行性能。 如下代码,就可以在编译时进行类型推导。
高阶泛型函数
编译器编译将得到如下代码:
下面,我们来看一个调用 apply 的例子。
由于能够在编译时推导出类型,上述代码就相当于如下代码:
然而,事实上,我们不能将以推导出的 Int (*func_invoke)(Int arg, void *ctxt) 闭包直接传递到 apply 中,而是需要将已确定类型的闭包转换成 void (*func_invoke)(opaque *ret, opaque *arg, void *ctxt) 闭包的形式间接传递到 apply 中。 那么,在确定类型的情况下,这里为什么仍然要做一次间接的转换与传递呢? 对此,我们需要首先介绍一个概念——重抽象(Reabstract)。 重抽象(Reabstract)Swift 中的所有类型都存在多个抽象级别。比如:一个 Int 值是一个具体类型,可以传递给确定类型的函数。但是 Int 值也可能传递给类型为泛型 T 的函数,此时该泛型函数希望能够间接接收该参数,从而适应其他可能的泛型类型,如:Float、String 等。当 Int 值传递给泛型函数时,它被认为比其为 Int 时处于更高的抽象级别。这种在抽象级别之间进行转化的过程被称为是 重抽象。
事实上,上述 apply 采用间接传递的思想,本质上就是为了实现对类型进行重抽象的一种抽象模式。 这里通过 thunk 来实现对闭包的值进行重抽象,从而匹配函数的抽象模式。
|
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 0:12:47- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |