前言

hello,小伙伴们:在忙碌中闲暇之余给大家聊聊swift的知识点,今天给大家带来的是swift中结构体与类的方法调度详细区别,希望对你有所帮助,好了废话不用多说,接下来步入主题!

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

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


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

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

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

extension PersonClass {
func changClassName10() {}
}

SIL代码:

断点,汇编跟踪一下:

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

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

我们在方法调度之普通方法一文中讲解过:函数表是数组结构,里面的函数是按顺序排列的

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

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

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

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

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

    private func changClassName2() {}
fileprivate func changClassName3() {}
public func changClassName4() {}
internal func changClassName5() {}
open func changClassName6() {}

SIL 代码:

sil_vtable SIL :

虽然所有函数修饰符修饰的方法,都在函数表中存在,但是明显 private 修饰的 changClassName2 , 与 fileprivate 修饰的changClassName3  与众不同,他们在方法名的后面有 in _12232F587A4C5CD8B1EEDF696793A4FC 。 这个不同,会导致它们在方法调度的时候,和其他的访问修饰符什么区别呢?

再看方法调度 SIL :

可以发现 private 修饰的 changClassName2 , 与 fileprivate 修饰的changClassName3  在调用时,前面的修饰符是由function_ref 修饰而不是class_method修饰。所以是静态派发? 

再汇编调试一下:

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

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

小结:

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

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

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

源码:

@objc func changClassName7() {}

vtable SIL:

方法调度 SIL:

运行、汇编:

所以: 在swift 中调用 @objc 修饰的方法是函数派发,没什么特别的。

那 @objc 的作用是什么呢?

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

可以看到,除了正常的定义changClassName7 方法以外,额外底层多生成了一个 @objc main.PersonClass.changClassName7()这个方法内部又调用了 正常定义的changClassName7。

所以这个方法是暴露给OC中调用的接口方法. 没有@objc 修饰的方法,OC 中是无法使用的。具体的混编步骤,以后会在 [Swift 与 OC 混编] 这篇文章中讲到

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

源码如下:

dynamic func changClassName8() {}

vtable SIL:

方法调度 SIL:

运行、汇编:

在编译时,不能确定方法的地址,在函数表内,所以dynamic的方法调度方式是函数派发。

dynamic 有什么作用呢?

看看方法定义SIL:

与普通函数不同的是,在方法定义时,多了一个dynamically_replacable的标签,表明这是一个动态方法,可以被替换。可被替换是指在OC运行时的方法交换的场景下可被替换。

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

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

示例代码如下:

class PersonClass: 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”

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

源码如下:

@objc dynamic func changClassName9() {}

vtable SIL:

函数表中没有changClassName9的函数。

方法调度 SIL:

与普通的函数派发方法调用时不同,不是以 class_method 方式,是以objc_method方式

运行、汇编调试:

汇编调试时,看到了熟悉的objc_msgSend。这是OC的消息转发的方式进行方法调度。

5. static 修饰

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

源码如下:

static func changClassName11() {}

vtable SIL:

方法调度 SIL:

运行、汇编调试:

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

6. final 修饰

final修饰符的几点使用原则

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

源码如下:

final func changClassName1() {}
table SIL:
 

方法调度 SIL:

行、汇编调试:

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

5. 总结

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

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

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

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

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

青山不改,绿水长流,后会有期,感谢每一位佳人的支持!



iOS Swift结构体与类的方法调度的更多相关文章

  1. Swift结构体与类

    在面向过程的编程语言(如C语言)中,结构体用得比较多,但是面向对象之后,如在C++和Objective-C中,结构体已经很少使用了.这是因为结构体能够做的事情,类完全可以取而代之.而Swift语言却非 ...

  2. Swift 结构体和类

    //*---------------------结构体-------------*/ //swift结构体也具有封装的概念 //swift结构体比OC C语言里面的结构体更加进了一步,里面可以有方法, ...

  3. 学习swift从青铜到王者之swift结构体和类08

    定义 // 定义类 class StudentC{ } // 定义结构体 struct StudentS{ } 定义存储属性 // 定义类 class StudentC{ var name:Strin ...

  4. Swift结构体和类

    Swift结构体 在Swift标准中,绝大多数的公开的类型都是结构体,而枚举和类只占很小的一部分 比如Bool.Int.Double.String.Array.Dictionary等常见的类型都是结构 ...

  5. Swift具体解释之六----------------枚举、结构体、类

    枚举.结构体.类 注:本文为作者自己总结.过于基础的就不再赘述 ,都是亲自測试的结果.如有错误或者遗漏的地方.欢迎指正,一起学习. 1.枚举 枚举是用来定义一组通用类型的一组相关值 ,关键字enum ...

  6. 10 结构体和类 - —— 《Swift3.0 从入门到出家》

    Swift中的面向对象5个要素:枚举.结构体.类.协议.扩展 面向对象研究的是对象,完成一件事情需要多个对象参与,是生活的映射 Swift中结构体和类非常相似,也就是结构体能完成类的所有功能.结构体是 ...

  7. C#中结构体和类的区别

    结构体和类同样能够定义字段,方法和构造函数,都能实例化对象,这样看来结构体和类的功能好像是一样的了,但是他们在数据的存储上是不一样的 C#结构体和类的区别问题:这两种数据类型的本质区别主要是各自指向的 ...

  8. Swift 结构体的使用

    Swift 结构体是构建代码所用的一种通用且灵活的构造体. 我们可以为结构体定义属性(常量.变量)和添加方法,从而扩展结构体的功能. 与 C 和 Objective C 不同的是: 结构体不需要包含实 ...

  9. 浅析C#中的结构体和类

    类和结构是 .NET Framework 中的常规类型系统的两种基本构造. 两者在本质上都属于数据结构.封装着一组总体作为一个逻辑单位的数据和行为. 数据和行为是该类或结构的"成员" ...

随机推荐

  1. ES6扩展——数值扩展

    1.0o代表八进制 0b代表二进制 ,通过Number()可转为10进制: //0o 0O octanary八进制 //0b 0B binary二进制 console.log(0o16); //14 ...

  2. jQuery mobile网格布局

    3.4 内容格式化 jQuery Mobile中提供了许多非常有用的工具与组件,如多列的网格布局.折叠形的面板控制等,这些组件可以帮助开发者快速实现正文区域内容的格式化. 3.4.1 网格布局 jQu ...

  3. 手机端rem简单配置相关

    手机端rem简单配置相关 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 ...

  4. 恶意软件开发——编写第一个Loader加载器

    一.什么是shellcode loader? 上一篇文章说了,我们说到了什么是shellcode,为了使我们的shellcode加载到内存并执行,我们需要shellcode加载器,也就是我们的shel ...

  5. Java的Class类及static块的执行时机

    要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工程由Class对象完成,它包含了与类有关的信息.Java使用Class对象来执行其RTTI,即使你执行的是类似转型 ...

  6. golang channel原理

    channel介绍 channel一个类型管道,通过它可以在goroutine之间发送和接收消息.它是Golang在语言层面提供的goroutine间的通信方式. 众所周知,Go依赖于称为CSP(Co ...

  7. 使用Keepalived实现Nginx的双机主备高可用

    1.概述 前面我们聊过使用 Nginx 为 后端Tomcat 做负载均衡.高可用,但是这时Nginx又成了单点,如果Nginx不幸挂掉,整个网站便无法访问. 此时我们就会用到另一个软件 -- Keep ...

  8. 痞子衡嵌入式:MCUXpresso IDE下工程链接文件配置管理与自动生成机制

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是MCUXpresso IDE下工程链接文件配置管理与自动生成机制. 痞子衡在 2018 年初写过一个专题 <嵌入式开发文件系列&g ...

  9. Golang入门学习(三):函数

    文章目录 2.3 函数 2.3.1 基本语法 2.3.2 入门demo: 2.3.3 函数递归: 2.3.4 函数注意事项 2.3.5 init函数 2.3.6 匿名函数 2.3.7 闭包 2.3.8 ...

  10. ubantu 编译安装xl2tpd笔记

    1. 下载xl2tpd源码 推荐一个仍在维护的网站:    https://www.xelerance.com/archives/202 也可以从其他的网站下载:例如:https://pkgs.org ...