最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Swift06 - ⽅法调度

    正文概述 掘金(良眸)   2021-02-02   774

    前言:

    上篇文章分析了Swift中的指针,以及指针Swift中的基本使用,这篇文章主要分析方法Swift中如何调用的,在swift中方法调度分为两种:

    • 静态调⽤(直接调用)

    • 查找调用(函数都按照顺序存储在vtable中,按照地址偏移方式进行调用)

    静态调⽤

    对于值类型对象的函数的调用方式是静态(直接)派发(调用),也可以说是直接地址调用(指针调用),也就是说,在编译,链接完成后当前函数的地址在Mach-O代码段就已经有了确定的位置。

    Swift中典型的值类型就是结构体,那么我们就通过如下的分析,证明结构体中函数的调用是静态调用。

    案例,在Xcode中创建:

    struct LGTeacher {
        func teach() {}
    }
    var t = LGTeacher()
    t.teach()
    print("end")
    

    通过汇编看函数的调用

    Swift06 - ⽅法调度

    在汇编的函数调用中,可以看到callq 直接调用的是0x100002c10地址,所以由此可看出Swift结构体的方法调用时直接地址派发

    Mach-O验证

    Mach-O中查看方法是否在__text段:

    Swift06 - ⽅法调度

    由上图可看出,方法的存储地址就在__text段中,所以可验证函数指针编译、链接完成后就已经确定了,存放在代码段

    符号表Symbol Table验证

    • 查看Functions Starts中所有的方法的重命名,命名规则:工程名+类名+函数名Swift06 - ⽅法调度

    • 查看符号表的地址偏移量

    Swift06 - ⽅法调度

    函数地址:0000000100002C10,和汇编中的地址一致

    • 查看Sting Table表

    Swift06 - ⽅法调度

    • 用终端查看重命名符合表中s12NewSwiftDemo9LGTeacherV5teachyyF符号

    Swift06 - ⽅法调度

    • 还原符号

    Swift06 - ⽅法调度

    动态派发

    既然有静态派发,那么当然就会有动态派发,在Swift中的引用类型数据代表中的函数调用就是动态派发

    知识补充

    • 汇编指令补充
    1. blr:带返回的跳转指令,跳转到指令后边跟随寄存器中保存的地址
    2. mov:将某一寄存器的值复制到另一寄存器(只能用于寄存器与起存起或者 寄存器与常量之间 传值,不能用于内存地址,mov x1,x0 将寄存器x0的值复制到寄存器x1中)
    3. ldr:将内存中的值读取到寄存器中(ldr x0,[x1,x2] 将寄存器x1和寄存器x2 相加作为地址,取该内存地址的值翻入寄存器x0中)
    4. str:将寄存器中的值写入到内存中(str x0, [x0, x8] 将寄存器x0的值保存到内存[x0 + x8]处)
    5. bl:跳转到某地址
    • vtable 简介

    Swift中的vtableSIL文件中的格式:

    //声明sil vtable关键字
    decl ::= sil-vtable
    //sil vtable中包含 关键字、标识(类名)、所有的方法
    2 sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
    //方法 包含了声明以及函数名称
    3 sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-na
    me
    

    以LGTacher为例,其SIL中的v-table如下所示:

    class LGTeacher{
        func teach(){}
        func teach2(){}
        func teach3(){}
        func teach4(){}
        @objc deinit{}
        init(){}
    }
    

    Swift06 - ⽅法调度

    函数表 可以理解为 数组,声明在 class内部的方法在不加任何关键字修饰的过程中,是连续存放在我们当前的地址空间中的,汇编断点调试:

    Swift06 - ⽅法调度

    由上图可了解到函数表的首地址是一片连续的内存地址

    源码分析

    上面由汇编的方式探究了vtable的内存存储方式,说明在vtable中肯定有其创建的过程,打开swift源码,全局搜索:

    Swift06 - ⽅法调度

    其内部是通过for循环编码,然后offset+index偏移,然后获取method,将其存入到偏移后的内存中,从这里可以印证函数是连续存放的,所以对于class中函数来说,类的方法调度是通过V-Taable,其本质就是一个连续的内存空间(数组结构)

    extension 中的方法

    创建LGTeacher的extension

    class LGTeacher{
        func teach(){}
        func teach1(){}
        func teach2(){}
        func teach3(){}
        func teach4(){}
    }
    extension LGTeacher {
        func teach5(){}
    }
    var t = LGTeacher()
    t.teach5()
    

    查看SIL文件中的VTable方法,看是否在编译过程中存入到函数表中

    Swift06 - ⽅法调度

    由上图可看出,extension的方法在编译时并未存储到vtable表中,那extension中的方法是如何调用的呢?查看汇编代码:

    Swift06 - ⽅法调度

     由上图可看出,extension直接callq的是0x100002b80函数地址,属于静态调用.

    final、@objc、dynamic修饰函数

    创建LGTeacherfinal,@objc,dynamic

    class LGTeacher{
      final func teach(){}
        func teach1(){}
      @objc  func teach2(){}
        func teach3(){}
      dynamic  func teach4(){}
    }
    extension LGTeacher {
        func teach5(){}
    }
    var t = LGTeacher()
    t.teach()
    t.teach2()
    t.teach4()
    t.teach5()
    
    • final

    查看SIL文件

    Swift06 - ⽅法调度

    可看出final修饰的函数并不在vtable中,查看汇编调用:

    Swift06 - ⽅法调度

    final关键字修饰的函数直接callq的是0x100002b80函数地址,属于静态调用.

    • @bjc

    通过sil文件我们可以发现,使用@objc修饰的方法依旧存在VTable中,特别提醒:

    Swift06 - ⽅法调度

    对于使用@objc修饰的方法,实际上是生成了两个方法,其中一个就是我们Swift中原有的方法,另一个就是如上面代码所示的@objc方法,并在其内部调用了Swift原有的方法。所以使用@objc修饰的方法本质是,通过sil代码中的@objc方法调用,Swift中的方法来实现的。

    • dynamic

    sil文件我们可以看到,使用dynamic修饰的方法是存放在VTable中的,目前没看到和swif中的方法有什么区别,官网中的定义为具有动态性.和@_dynamicReplacement关键字一起使用可以进行方法交换

    Swift06 - ⽅法调度

    • @objc + dynamic

    Swift06 - ⽅法调度

    汇编断点调试:

    Swift06 - ⽅法调度

    可以看出走的是objc_msgSend流程,即 动态消息转发

    总结

    • Swift中的方法调用分为静态调度动态调度两种

    • 值类型(例如struct)中的方法就是静态调度

    • 引用类型中的方法就是动态调度,其中函数的调度是通过V-Table函数表来进行调度的,即动态调度

    • extensionfinal中的函数调度方式是静态调度

    • @objc修饰的函数调度方式是函数表调度,如果OC中需要使用,class还必须继承NSObject

    • dynamic修饰的函数的调度方式是函数表调度,使函数具有动态性

    • 使用dynamic@objc同时修饰的方法的调度方式是objc_msgSend


    起源地下载网 » Swift06 - ⽅法调度

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元