iOSSwift好文收藏Swift学习

swift的witness table

2018-10-08  本文已影响542人  sea_biscute

V-table和witness table

我们知道,执行方法时,首先要查找到正确的方法,然后执行.能够在编译期确定执行方法的方式叫做静态分派static dispatch,无法在编译期确定,只能在运行时去确定执行方法的分派方式叫做动态分派dynamic dispatch.
静态分派更快,而且静态分派可以进行内联等进一步的优化操作,使得执行更快速,性能更高.
但是对于多态的情况,我们不能在编译期确定最终的类型,这里就用到了dynamic dispatch动态分派.动态分派的实现是,每种类型都会创建一张表,表内是一个包含了方法指针的数组.
对于类class来说,每个类型都会创建虚函数表指针,指向一个叫做V-Table的表.拥有继承关系的子类会在虚函数表内通过继承顺序(C++可以实现多继承)去展示虚函数表指针.
但是对于swift来说,class类和struct结构体的实现是不同的,而属于结构体的协议Protocol,可以拥有属性和实现方法,管理Protocol Type方法分派的表就叫做Protocol Witness Table.

witness table内部结构

witness table
和V-table一样,Protocol Witness Table(简称PWT)内存储的是方法数组,里面包含了方法实现的指针地址,一般我们调用方法时,是通过获取对象的内存地址和方法的位移offset去查找的.
Protocol Witness Table是用于管理Protocol Type的方法调用的,在我们接触swift性能优化时,听到另一个概念叫做Value Witness Table(简称VWT),这个又是做什么的呢?

什么是value witness table

value witness table
value witness table的结构如上,是用于管理遵守了协议的Protocol Type实例的初始化,拷贝,内存消减和销毁的.
value witness table还可以拆分为%relative_vwtable%absolute_vwtable,我们这里先不做展开,之后会对这部分内容进行补充.
value witness tableprotocol witness table通过分工,去管理Protocol Type实例的内存管理(初始化,拷贝,销毁)和方法调用.

说完witness table的结构,我们讨论一下,在编译器内部实现中,SIL(swift intermediate language)内部是如何实现vtable和witness table的.

V-Table在SIL的实现

decl ::= sil-vtable
sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'

sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-name

SIL使用 class_method, super_method, objc_method, 和 objc_super_method 操作来实现类方法的动态分派.
类的每一个方法,都被映射到SIL的方法实现

class A {
  func foo()
  func bar()
  func bas()
}

sil @A_foo : $@convention(thin) (@owned A) -> ()
sil @A_bar : $@convention(thin) (@owned A) -> ()
sil @A_bas : $@convention(thin) (@owned A) -> ()

sil_vtable A {
  #A.foo!1: @A_foo
  #A.bar!1: @A_bar
  #A.bas!1: @A_bas
}

class B : A {
  func bar()
}

sil @B_bar : $@convention(thin) (@owned B) -> ()

sil_vtable B {
  #A.foo!1: @A_foo
  #A.bar!1: @B_bar
  #A.bas!1: @A_bas
}

class C : B {
  func bas()
}

sil @C_bas : $@convention(thin) (@owned C) -> ()

sil_vtable C {
  #A.foo!1: @A_foo
  #A.bar!1: @B_bar
  #A.bas!1: @C_bas
}

需要注意的是,vtable中的方法声明是指向最后的衍生类的方法的.swift的AST持有了声明的重载关系,并用于在SIL的vtable中查找衍生类的重载方法.
为了防止SIL的方法是thunk,方法名使用了原始方法实现的连接(linkage)作为前缀.

Witness Tables在编译期内SIL阶段的实现

decl ::= sil-witness-table
sil-witness-table ::= 'sil_witness_table' sil-linkage?
                      normal-protocol-conformance '{' sil-witness-entry* '}'

SIL将泛型动态分派所需的信息编码为witness表.这些信息用于在生成二进制码时产生运行时分配表(runtime dispatch table).也可以用于对特定通用函数的SIL优化.每个明确的一致性声明都会产生witness表.通用类型的所有实例共享一个通用witness表.衍生类会继承基类的witness表.

protocol-conformance ::= normal-protocol-conformance //一般协议一致性
protocol-conformance ::= 'inherit' '(' protocol-conformance ')'  //继承关系的协议一致性
protocol-conformance ::= 'specialize' '<' substitution* '>'    //通用类型特化的协议一致性和标明类型降级的替换(substitution)
                         '(' protocol-conformance ')'
protocol-conformance ::= 'dependent'
normal-protocol-conformance ::= identifier ':' identifier 'module' identifier

witness的关键在于协议一致性.它是对于具体类型协议一致性的唯一标识.

sil-witness-entry ::= 'base_protocol' identifier ':' protocol-conformance
sil-witness-entry ::= 'method' sil-decl-ref ':' sil-function-name
sil-witness-entry ::= 'associated_type' identifier
sil-witness-entry ::= 'associated_type_protocol'
                      '(' identifier ':' identifier ')' ':' protocol-conformance

witness table由以下内容构成

参考

上一篇下一篇

猜你喜欢

热点阅读