swift 函数派发方式

347 阅读2分钟

常用汇编指令

在学习函数派发之前,我们先简单了解一下arm64的一些汇编指令

  • mov:将某一寄存器的值复制到另一寄存器
//将寄存器x0的值复制到寄存器x1中
mov x1, x0 
  • add:将某一寄存器的值和另一寄存器的值相加,并将结果保存在另一个寄存器
//将寄存器x1和x2的值相加后保存到寄存器x0中
add x0,x1,x2 
  • sub:将某一寄存器的值和另一寄存器的值相减,并将结果保存在另一个寄存器
//将寄存器x1和x2的值相减后保存到寄存器x0中
sub x0,x1,x2
  • str:将寄存器中的值写入到内存中
//将寄存器x0中的值保存到栈内存[x0 + x8]处
str x0, [x0,x8]
  • ldr:将内存中的值读取到寄存器中
//将寄存器x1和寄存器x2的值相加作为地址,取该内存地址的值放入到寄存器x0中
ldr x0,[x1,x2] 
  • blr:跳转到某地址(无返回) (函数无返回值)
  • bl:跳转到某地址(有返回) (函数有返回值)

函数的返回值一般是放在x0寄存器中的

函数调用分析

class

class Teacher{
    func test(){
        print("test")
    }
    func test2(){
        print("test2")
    }
}
let teacher = Teacher()
teacher.test()
teacher.test2()

汇编分析teacher.test() Xnip2022-02-06_15-40-56.jpg 通过上面的分析,我们可以得出test函数的调用过程:找到metadata,确定函数地址偏移量(metadata + 偏移量),执行函数

SIL分析teacher.test() Xnip2022-02-06_15-49-31.jpg sil_vtable 是每个函数表

struct

struct StructPerson{
    func test(){
        print("test")
    }
}

汇编分析 Xnip2022-02-06_16-46-29.jpg 也可以通过sil分析,struct中,并未生成sil_vtable

Enum

enum EnumStudent{
    case A,B
    func test(){
        switch self{
        case .A:
             print("A")
        case .B:
             print("B")
        }
    }
}

汇编分析 Xnip2022-02-06_16-50-42.jpg

总结

Xnip2022-02-06_22-45-59.jpg

影响函数派发方式(针对的都是class类型)

  • extension静态派发
class Teacher{
    func test(){
        print("test")
    }
}
extension Teacher{
    func test2(){
        print("test2")
    }
}
let t = Teacher()
t.test()
t.test2()

我们知道t.test()的函数调用函数表调用,但是test2()呢?我们通过汇编得到如下: Xnip2022-02-06_22-11-22.jpg 由上面汇编代码可以看出,extension里面的test2()函数成了直接调用,也就是说extension 不会影响类的vtable结构,extension里面的方法使用静态派发的方式

  • 添加了final关键字的函数无法被重写,使用静态派发的方式,不会出现在vtable中
class Teacher{
    final func test(){
        print("test")
    }
}
let t = Teacher()
t.test()

Xnip2022-02-06_22-58-28.jpg Xnip2022-02-06_22-59-34.jpg

final的使用:在开发中,当我们的类,属性,方法不需要被重载时,可以添加final关键字
  • @objc dynamic 消息派发的方式
class Teacher{
    @objc dynamic func test(){
        print("test")
    }
}
let t = Teacher()
t.test()

Xnip2022-02-06_22-41-48.jpg

  • dynamic: 函数均可添加 dynamic 关键字,为非objc类和值类型的函数赋予动态性,但派发方式还是函数表派发。
  • @objc: 该关键字可以将Swift函数暴露给Objc运行时,依旧是函数表派发。