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