swift类方法为什么使用表派发?

直接上答案:因为表派发允许子类重写父类的方法,并在运行时根据对象的实际类型调用正确的方法实现。

什么是表派发?

首先我们先知道的是,swift当中函数的派发机制主要分为静态派发和动态派发。动态派发又分为表派发和消息派发。

静态派发(直接派发)

静态派发的速度非常快,因为编译器能在编译时定位指令所在的位置(编译时就会绑定方法)。因此函数被调用时候,编译器直接跳转到函数的内存地址来执行操作。所以就会非常快。使用静态派发的主要是所有的值类型,扩展的方法,以及用final,static的修饰的类和属性。(由于stuct和enum是值类型,且不支持继承,所以编译器把方法放在静态派发)

扩展的方法为什么也是静态派发?

这里我们首先要明白扩展的本质:扩展的用途是为现的类型添加新的功能,而不修改原有类型的定义。

·扩展的方法在编译期间就会被静态的绑定到类型本身

·扩展不能参与类型的继承链

·扩展没有动态和多态,不能覆盖原有类型或字类中的方法。

扩展(extension)不会额外增加运行时的存储空间,因为扩展的本质是在编译期对类型的功能进行静态增强,而不是动态地向类型添加新成员(存储属性会改变类型的内存布局,而扩展被设计为一种编译时功能增强机制。如果允许扩展添加存储属性,会导致类型的内存布局不确定,影响已有代码的兼容性)。扩展是对类型的附加行为,扩展方法不参与类的继承体系,因此不能支持动态派发(多态)。

表(table)派送

表派送只要是利用一个表,该表是一组函数指针,成为witness table(虚拟表),实际上就是swift的运行时表,用来查找特定方法的实现。

witness如何工作的呢?

·每个子类都有自己对于此表的拷贝

·对于此类重写的每个方法,此表都有不同的函数指针

·对于子类添加新方法时,这些方法指针将附加到数组的结尾。

·最后编译器在表中查看为方法调用的具体实现

由于编译器必须从表中读取方法实现的内存地址,然后跳转地址,由于要执行两条指令,所以他比静态派发要慢,但比Message要快。使用表派发的主要是类的实例方法和子类可重写的方法。

消息(Message)派发

消息派发是一种动态派发机制,通过运行时的消息传递来调用方法。他不是直接调用方法的内存地址,而是把消息发送给对象,由对象动态解析方法。在swift中,消息派发主要是支持OC的动态特性,例如方法替换等(Method Swizzing)和运行时(runtime)消息解析。主要用于@objc和dynamic的方法。

回到最开始的为什么要使用表派发?主要是因为支持class的继承和方法的重写(多态),因为这些需要根据对象的实际类型来调用方法。静态派发的话,对象的动态类型无法在编译期间确定。

相关推荐
东坡肘子2 天前
Swift 还让你 Excited 吗?-- 肘子的 Swift 周报 #141
人工智能·swiftui·swift
sweet丶5 天前
Swift 元编程-Macro
swift
影寂ldy8 天前
WinForm PictureBox控件 + ImageList组件 完整笔记
开发语言·笔记·swift
Deepzz9 天前
macOS 上调教第三方鼠标的一些经验:从滚动顺滑到输入法自动切换
macos·swift·鼠标
东坡肘子9 天前
WWDC 26:AI 帮你看完了,然后呢?-- 肘子的 Swift 周报 #140
人工智能·swiftui·swift
大熊猫侯佩10 天前
SwiftData 迁移深度指南:从入门到“填坑”(下集)
数据库·swift·编程语言
大熊猫侯佩10 天前
SwiftData 迁移深度指南:从入门到“填坑”(上集)
数据库·swift·编程语言
多彩电脑10 天前
SwiftUI的导航界面的嵌套问题
开发语言·swift·设计语言
wjm04100611 天前
ios内存管理
ios·objective-c·swift·客户端开发
大熊猫侯佩11 天前
Swift 6.4 的 Ref / MutableRef 大揭秘:给值类型开一扇“安全的小窗”
ios·swift·编程语言