Swift结构体与类的方法调度

前言

在忙碌中抽取时间来看这篇文章的朋友,希望各位看完这篇问斩都能都有所收获。这篇文章主要是给大家分享的是Swift结构体与类的方法调度。好了废话不多说,接下来直接步入主题吧。

1.普通方法时两者方法调度的区别

● 结构体中的普通方法调度是静态派发的方式
   ○ 详细分析会在以后: 方法调度之普通结构体方法 阐述
● 类中的普通方法是以函数派发的方式去调度的。
   ○ 详细分析会在以后:方法调度之普通方法 阐述

2.议中两者方法调度的区别

● 以类/结构体直接声明的,
  ○ 结构体:方法调度都是静态调度
  ○ 类:方法调度都是函数调度
● 以协议类型声明的, 无论协议的实现是类还是结构体:
  ○ 方法最初定义在协议本身内, 则方法以协议函数表的方式调度
  ○ 方法最初定义在协议延展内, 则方法以静态派发的方式调度

3.extension对类中方法调度的影响

``extension PersonClass {

``func changClassName10``() {}

}

SIL代码:

[图片上传失败...(image-1dcbb3-1634395436453)]

[图片上传失败...(image-a6e24-1634395436453)]

断点,汇编跟踪一下:

[图片上传失败...(image-50578a-1634395436453)]

可以看到 changClassName10 这个方法在执行的时候,由function_ref修饰,sil_vtable 中的函数列表里面没有。在编译时已经确定了函数的地址,运行时,直接执行。所以延展内的方法是静态派发。

🤔****思考: 为什么普通函数放到了延展中,它就不在函数表中,不是函数派发的方式调度了呢?

我们在[方法调度之普通方法:后续笔者会发布普通方法调用过程]:函数表是数组结构,里面的函数是按顺序排列的

如果父类存在延展方法,且放在函数表里,就需要考虑它和子类方法的排列顺序问题。哪个在前,取决于文件的编译顺序。如果子类先编译,父类后编译,还要将子类的所有方法都顺次移位,再将延展方法插入到父类方法之后。这样做,编译效率就会降低将延展方法使用静态派发,是一种以空间换时间的方法。 协议的延展中的方法,也是静态派发的,他们是一样的道理。

【注意】类的延展方法时,需要注意:

  • 不可以在子类里重写父类延展里面的方法,子类可以重写父类本类定义的方法
  • 不可以在延展里 存在/重写 已在继承连中存在的同名方法

[图片上传失败...(image-5410e6-1634395436453)]

4.修饰词对类方法调度的影响

1. 访问修饰符修饰的方法

private func changClassName2``() {}

fileprivate func changClassName3``() {}

public func changClassName4``() {}

internal func changClassName5``() {}

open func changClassName6``() {}

SIL 代码:
sil_vtable SIL :

[图片上传失败...(image-5ddce8-1634395436453)]
虽然所有函数修饰符修饰的方法,都在函数表中存在,但是明显 private 修饰的 changClassName2 , 与 fileprivate 修饰的changClassName3 与众不同,他们在方法名的后面有 in _12232F587A4C5CD8B1EEDF696793A4FC ****。 这个不同,会导致它们在方法调度的时候,和其他的访问修饰符什么区别呢?
再看方法调度 SIL :

[图片上传失败...(image-b50363-1634395436453)]
可以发现 private 修饰的 changClassName2 , 与 fileprivate 修饰的changClassName3 在调用时,前面的修饰符是由function_ref 修饰 而不是class_method修饰。所以是静态派发?

再汇编调试一下:

[图片上传失败...(image-8148de-1634395436453)]

编译时已经确定了函数的地址,运行时,直接执行。所以private/fileprivate 访问修饰符修饰的是静态派发。

前面我们提到“函数表存放类中可能是动态派发去执行的函数”, 注意是可能哦, 不是一定的

小结:

private/fileprivate 访问修饰符修饰的是静态派发。

public/open/internal 访问修饰符修饰的是函数派发。

2. @objc 修饰的方法: 函数表

源码:@objc func changClassName7``() {}
vtable SIL:

[图片上传失败...(image-d6971f-1634395436453)]
方法调度 SIL:

[图片上传失败...(image-cdcda3-1634395436453)]
运行、汇编:

[图片上传失败...(image-a031ef-1634395436453)]
所以: 在swift 中调用 @objc 修饰的方法是函数派发,没什么特别的。

@objc 的作用是什么呢?

我们来看一下changClassName7 方法定义在 SIL 代码:

[图片上传失败...(image-b852b5-1634395436453)]
可以看到,除了正常的定义changClassName7 方法以外,额外底层多生成了一个 @objc main.PersonClass.changClassName7()这个方法内部又调用了 正常定义的changClassName7。

所以这个方法是暴露给OC中调用的接口方法. 没有@objc 修饰的方法,OC 中是无法使用的。

3. dynamic 修饰的方法:函数表

源码如下:
dynamic func changClassName8``() {}
vtable SIL:

[图片上传失败...(image-f62e21-1634395436453)]
方法调度 SIL:

[图片上传失败...(image-7b8f92-1634395436453)]
运行、汇编:

[图片上传失败...(image-3bbd4f-1634395436453)]
在编译时,不能确定方法的地址,在函数表内,所以 dynamic的方法调度方式 是函数派发。

dynamic 有什么作用呢?

看看方法定义SIL:

[图片上传失败...(image-b25c4b-1634395436453)]
与普通函数不同的是,在方法定义时,多了一个dynamically_replacable的标签,表明这是一个动态方法,可以被替换。可被替换是指在OC运行时的方法交换的场景下可被替换。

如果想要对Swift 方法进行方法交换,需要对被替换的方法加dynamic修饰。

再使用@_dynamicReplacement(for: teach)来完成替换.

示例代码如下:

````classPersonClass:NSObject{`

``dynamic func teach``() {

``print``(``"teach"``)

``}

}

extension PersonClass {

``// swift 5 中提供的方法交换方式

``// 将 teach 方法替换成这行代码下面的teach1方法

``// 执行 teach 方法,实际上执行的是 teach1方法

``@``_dynamicReplacement``(``for``: ``teach``)

``func teach1``() {

``print``(``"teach1"``)

``}

}

let t = ``PersonClass``()

t.teach()

所以打印结果是:“teach1”

[图片上传失败...(image-f74690-1634395436453)]

4. @objc dynamic 修饰的方法:消息转发

源码如下:

1 @objc dynamic func changClassName9``() {}

vtable SIL:

[图片上传失败...(image-9721e7-1634395436453)]

函数表中没有changClassName9的函数。

方法调度 SIL:

[图片上传失败...(image-5abe6-1634395436453)]
与普通的函数派发方法调用时不同,不是以 class_method 方式,是以objc_method方式

运行、汇编调试:

[图片上传失败...(image-5b1001-1634395436453)]
汇编调试时,看到了熟悉的objc_msgSend 。这是OC的消息转发的方式进行方法调度。

5. static 修饰

static修饰的方法,叫做类方法,可以直接由类名去调用,无需创建实例对象。

源码如下:

1 static func changClassName11``() {}

vtable SIL:

[图片上传失败...(image-21da64-1634395436453)]

方法调度 SIL:

[图片上传失败...(image-8a3771-1634395436453)]

运行、汇编调试:

[图片上传失败...(image-34e219-1634395436453)]
以function_ref 的方式获取函数, 所以是静态派发

6. final 修饰

final修饰符的几点使用原则

  • final修饰符只能修饰,表明该类不能被其他类继承,也就是它没资格当父类。
  • final修饰符也可以修饰类中的方法, 表明该方法不能被子类重写
  • final不能修饰结构体、枚举、协议。

源码如下:
源码如下:

1 final func changClassName1``() {}

table SIL:

[图片上传失败...(image-f3ceea-1634395436453)]

方法调度 SIL:

[图片上传失败...(image-d02d0a-1634395436453)]

行、汇编调试:

[图片上传失败...(image-807e0b-1634395436453)]

以function_ref 的方式获取函数, 所以是静态派发

5.总结

函数表内的函数,不一定是函数派发的方式去调度。但是不在函数表中的,一定不是函数派发的方式。

在调用时获取函数的方式可以作为判断调度方法的依据。下面是对应不同的获取函数的方式的不同调度方式:

Swift 中的方法调度分2大类:动态 调度 与静态 调度

  • Direct(静态调度): 在 SIL 文件中,以function_ref 的方式获取函数
    • 🌰 结构体的普通方法
    • 类中方法的修饰符为 final / private/fileprivate / static
    • 类、结构体、协议延展内的方法
  • Dynamic Dispatch( 动态调度 官方文档传送门☞ Dynamic Dispatch
    • Table(函数表调度) : 在 SIL 文件中,以 class_method 的方式,通过 Vtable 获取函数
    • 🌰 普通类中的方法
    • 类中方法的修饰符为: open/public/internal / @objc / dynamaic
    • Message(消息转发调度): 在 SIL 文件中,以 objc_method 的方式获取函数
    • 🌰 @objc dynamaic
    • witness_method(协议表调度): 在 SIL 文件中,以 witness_method 的方式, 通过 PWT 获取函数
    • 🌰 遵守了协议并实现了协议本身定义的方法的结构体或者类

好了,小编给大家整理的swift的结构体与类的方法调度,若有收获,就点个赞吧!

青山不改,绿水长流,后会有期,感谢每一位佳人的支持!更多iOS知识请关注主页。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容