# 26.Swift方法调度

## 纯 Swift 类

### 普通函数

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

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

生成 SIL 文件，找到：

```C
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 关键字修饰的函数

对上面代码进行修改

```Swift
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:**

```C
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](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-0c42df04a483972bb1b28525792ca666e6746b85%2F02-01.png?alt=media)

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

### extension

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

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

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

```Swift
->  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 中的函数是 **静态调用** 的。

### 子类

我们定义一个子类：

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


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

SIL:

```C
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
}
```

汇编端点：

```c
    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

如果重写父类方法呢？

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

SIL 会有些许变化：

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

### final

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

> private fileprivate 的会被自动优化为 final

## sil\_vtable

我们找到 `initClassVTable` 源码：

```C++
/// 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` 内部是一个数组的结构，通过下标读写。

## 结构体

```swift
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
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/swift/02.swift-fang-fa-tiao-du.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
