26.Swift方法调度

纯 Swift 类

普通函数

class RyukieSwiftClass {
    var age: String?
    
    func hello() {
        print(age ?? "-")
    }
}

let ry = RyukieSwiftClass()
ry.age = "Ryukie"
ry.hello()

生成 SIL 文件,找到:

sil_vtable RyukieSwiftClass {
  #RyukieSwiftClass.age!getter: (RyukieSwiftClass) -> () -> String? : @main.RyukieSwiftClass.age.getter : Swift.String?	// RyukieSwiftClass.age.getter
  #RyukieSwiftClass.age!setter: (RyukieSwiftClass) -> (String?) -> () : @main.RyukieSwiftClass.age.setter : Swift.String?	// RyukieSwiftClass.age.setter
  #RyukieSwiftClass.age!modify: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.age.modify : Swift.String?	// RyukieSwiftClass.age.modify
  #RyukieSwiftClass.hello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.hello() -> ()	// RyukieSwiftClass.hello()
  #RyukieSwiftClass.init!allocator: (RyukieSwiftClass.Type) -> () -> RyukieSwiftClass : @main.RyukieSwiftClass.__allocating_init() -> main.RyukieSwiftClass	// RyukieSwiftClass.__allocating_init()
  #RyukieSwiftClass.deinit!deallocator: @main.RyukieSwiftClass.__deallocating_deinit	// RyukieSwiftClass.__deallocating_deinit
}

可见:纯 Swift 类的函数,是存在一个 sil_vtable 类型的数据结构内的。

@objc、dynamic、@objc dynamic 关键字修饰的函数

对上面代码进行修改

class RyukieSwiftClass {
    var age: String?
    
    func hello() {
        print("hello: \(age ?? "-")")
    }
    
    dynamic func dynamicHelo() {
        print("dynamic: \(age ?? "-")")
    }
    
    @objc func atObjcHello() {
        print("@objc: \(age ?? "-")")
    }
    
    @objc dynamic func atObjcDynamicHello() {
        print("@objc dynamic: \(age ?? "-")")
    }
}

let ry = RyukieSwiftClass()
ry.age = "Ryukie"
ry.hello()
ry.dynamicHelo()
ry.atObjcHello()
ry.atObjcDynamicHello()

SIL:

sil_vtable RyukieSwiftClass {
  #RyukieSwiftClass.age!getter: (RyukieSwiftClass) -> () -> String? : @main.RyukieSwiftClass.age.getter : Swift.String?	// RyukieSwiftClass.age.getter
  #RyukieSwiftClass.age!setter: (RyukieSwiftClass) -> (String?) -> () : @main.RyukieSwiftClass.age.setter : Swift.String?	// RyukieSwiftClass.age.setter
  #RyukieSwiftClass.age!modify: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.age.modify : Swift.String?	// RyukieSwiftClass.age.modify

// 普通函数
  #RyukieSwiftClass.hello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.hello() -> ()	// RyukieSwiftClass.hello()

// dynamic 函数
  #RyukieSwiftClass.dynamicHelo: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.dynamicHelo() -> ()	// RyukieSwiftClass.dynamicHelo()

// @objc 函数
  #RyukieSwiftClass.atObjcHello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.atObjcHello() -> ()	// RyukieSwiftClass.atObjcHello()

  #RyukieSwiftClass.init!allocator: (RyukieSwiftClass.Type) -> () -> RyukieSwiftClass : @main.RyukieSwiftClass.__allocating_init() -> main.RyukieSwiftClass	// RyukieSwiftClass.__allocating_init()
  #RyukieSwiftClass.deinit!deallocator: @main.RyukieSwiftClass.__deallocating_deinit	// RyukieSwiftClass.__deallocating_deinit
}

通过 SIL 我们发现:普通函数@objc 函数dynamic 函数 都是通过 sil_vtable 存储的,而且内存空间连续。

但是 sil_vtable 内部少了 @objc dynamic 修饰的函数。我们通过汇编断点看一下:

图中我们发现 @objc dynamic 修饰的函数,是通过 objc_msgSend 进行调度的,所以并不在 sil_vtable 内。

extension

我们新增一个 extension 并调用其中的函数:

extension RyukieSwiftClass {
    func extensionHello() {
        print("extension: \(age ?? "-")")
    }
}

汇编端点发现 call 0x1000037e0 是直接通过地址调用的:

->  0x1000029d9 <+73>: mov    r13, qword ptr [rip + 0x58a0] ; SourceCodeStudy.ry : SourceCodeStudy.RyukieSwiftClass
    0x1000029e0 <+80>: call   0x1000037e0               ; SourceCodeStudy.RyukieSwiftClass.extensionHello() -> () at main.swift:35

且在 sil_vtable 结构中没有发现 extensionHello

所以:在 Swift 类的 extension 中的函数是 静态调用 的。

子类

我们定义一个子类:

class RyukieSwiftSubClass: RyukieSwiftClass {
    func subHello() {
        print("sub: \(age ?? "-")")
    }
}


let subRy = RyukieSwiftSubClass()
subRy.age = "18"
subRy.hello()
subRy.subHello()
subRy.extensionHello()

SIL:

sil_vtable RyukieSwiftSubClass {
// 父类的结构
  #RyukieSwiftClass.age!getter: (RyukieSwiftClass) -> () -> String? : @main.RyukieSwiftClass.age.getter : Swift.String? [inherited]	// RyukieSwiftClass.age.getter
  #RyukieSwiftClass.age!setter: (RyukieSwiftClass) -> (String?) -> () : @main.RyukieSwiftClass.age.setter : Swift.String? [inherited]	// RyukieSwiftClass.age.setter
  #RyukieSwiftClass.age!modify: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.age.modify : Swift.String? [inherited]	// RyukieSwiftClass.age.modify
  #RyukieSwiftClass.hello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.hello() -> () [inherited]	// RyukieSwiftClass.hello()
  #RyukieSwiftClass.dynamicHelo: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.dynamicHelo() -> () [inherited]	// RyukieSwiftClass.dynamicHelo()
  #RyukieSwiftClass.atObjcHello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftClass.atObjcHello() -> () [inherited]	// RyukieSwiftClass.atObjcHello()

// 自己的结构
  #RyukieSwiftClass.init!allocator: (RyukieSwiftClass.Type) -> () -> RyukieSwiftClass : @main.RyukieSwiftSubClass.__allocating_init() -> main.RyukieSwiftSubClass [override]	// RyukieSwiftSubClass.__allocating_init()
  #RyukieSwiftSubClass.subHello: (RyukieSwiftSubClass) -> () -> () : @main.RyukieSwiftSubClass.subHello() -> ()	// RyukieSwiftSubClass.subHello()
  #RyukieSwiftSubClass.deinit!deallocator: @main.RyukieSwiftSubClass.__deallocating_deinit	// RyukieSwiftSubClass.__deallocating_deinit
}

汇编端点:

    0x100002546 <+182>: mov    rax, qword ptr [rip + 0x5e9b] ; SourceCodeStudy.subRy : SourceCodeStudy.RyukieSwiftSubClass
    0x10000254d <+189>: mov    r13, rax
    0x100002550 <+192>: mov    rax, qword ptr [rax]
    0x100002553 <+195>: call   qword ptr [rax + 0x70]
    0x100002556 <+198>: mov    r13, qword ptr [rip + 0x5e8b] ; SourceCodeStudy.subRy : SourceCodeStudy.RyukieSwiftSubClass
    0x10000255d <+205>: mov    rax, qword ptr [r13]
    0x100002561 <+209>: call   qword ptr [rax + 0x90]
->  0x100002567 <+215>: mov    r13, qword ptr [rip + 0x5e7a] ; SourceCodeStudy.subRy : SourceCodeStudy.RyukieSwiftSubClass
    0x10000256e <+222>: call   0x100003370               ; SourceCodeStudy.RyukieSwiftClass.extensionHello() -> () at main.swift:35

我们发现子类的 sil_vtable 结构中包含了父类的结构,自己专有的部分跟在父类的结构之后。

子类的方法调度,没有特殊的方法变化。

override

如果重写父类方法呢?

override func hello() {
    print("override hello: \(age ?? "-")")
}

SIL 会有些许变化:

sil_vtable RyukieSwiftSubClass {
// ...
  #RyukieSwiftClass.hello: (RyukieSwiftClass) -> () -> () : @main.RyukieSwiftSubClass.hello() -> () [override]	// RyukieSwiftSubClass.hello()
// ...
}

final

静态调度,不会存在于 vtable 中

private fileprivate 的会被自动优化为 final

sil_vtable

我们找到 initClassVTable 源码:

/// Using the information in the class context descriptor, fill in in the
/// immediate vtable entries for the class and install overrides of any
/// superclass vtable entries.
static void initClassVTable(ClassMetadata *self) {
  const auto *description = self->getDescription();
  auto *classWords = reinterpret_cast<void **>(self);

  if (description->hasVTable()) {
    // 遍历添加 method,数组结构
    auto *vtable = description->getVTableDescriptor();
    auto vtableOffset = vtable->getVTableOffset(description);
    for (unsigned i = 0, e = vtable->VTableSize; i < e; ++i)
      classWords[vtableOffset + i] = description->getMethod(i);
  }

  if (description->hasOverrideTable()) {
    // 遍历处理 override 的 method
    auto *overrideTable = description->getOverrideTable();
    auto overrideDescriptors = description->getMethodOverrideDescriptors();

    for (unsigned i = 0, e = overrideTable->NumEntries; i < e; ++i) {
      auto &descriptor = overrideDescriptors[i];

      // Get the base class and method.
      auto *baseClass = descriptor.Class.get();
      auto *baseMethod = descriptor.Method.get();

      // If the base method is null, it's an unavailable weak-linked
      // symbol.
      if (baseClass == nullptr || baseMethod == nullptr)
        continue;

      // Calculate the base method's vtable offset from the
      // base method descriptor. The offset will be relative
      // to the base class's vtable start offset.
      auto baseClassMethods = baseClass->getMethodDescriptors().data();
      auto offset = baseMethod - baseClassMethods;

      // Install the method override in our vtable.
      auto baseVTable = baseClass->getVTableDescriptor();
      classWords[baseVTable->getVTableOffset(baseClass) + offset]
        = descriptor.Impl.get();
    }
  }
}

通过源码我们发现 sil_vtable 内部是一个数组的结构,通过下标读写。

结构体

struct RyukieStruct {
    func hello() {
        print("hello")
    }
    
    dynamic func dynamicHelo() {
        print("dynamic")
    }
}

extension RyukieStruct {
    func extensionHello() {
        print("extension")
    }
}

let st = RyukieStruct()
st.hello()
st.dynamicHelo()
st.extensionHello()

汇编断点:

    0x100005ba9 <+9>:  call   0x100005bc0               ; SourceCodeStudy.RyukieStruct.hello() -> () at main.swift:15
    0x100005bae <+14>: call   0x100005c80               ; SourceCodeStudy.RyukieStruct.dynamicHelo() -> () at main.swift:19
->  0x100005bb3 <+19>: call   0x100005d80               ; SourceCodeStudy.RyukieStruct.extensionHello() -> () at main.swift:25

发现都是通过地址直接静态调用的。

Last updated