1183 字
6 分钟
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发现都是通过地址直接静态调用的。