runtime底层实现原理: blog.csdn.net/yahibo/arti…
runtime(一)------类结构,方法结构,方法缓存等: blog.csdn.net/liujingran_…
OC中Runtime方法缓存: www.jianshu.com/p/7cbd77e0a…
crash收集: www.jianshu.com/p/05aad21e3… blog.csdn.net/u013602835/… blog.csdn.net/weixin_3422…
swift runtime:www.jianshu.com/p/cfd56c76f…
swift 类的结构:www.jianshu.com/p/093457ec5…
swift 中类与结构体:www.sohu.com/a/615609825…
1、Swift 中的消息调用
Swift 中的消息调用是通过 运行时(runtime)实现的。运行时是指程序运行时所处的环境,它提供了许多在编译时无法确定的功能,例如动态分配内存、检查类型、调用方法等。
在 Swift 中,每个类都有一个名为 isa(instance specific attributes)的指针,指向该类的 元类(metaclass)。元类包含有关该类的信息,例如类名、方法列表、属性列表等。
当调用一个对象的方法时,编译器会生成以下汇编代码:
mov eax, [esp+4] ; 获取 self 指针
mov edx, [eax+0] ; 获取 isa 指针
mov ecx, [METHOD_ADDR] ; 获取方法地址
jmp ecx ; 调用方法
其中:
esp+4是栈顶指针,指向 self 对象eax存储 self 指针[eax+0]获取 isa 指针METHOD_ADDR是方法地址ecx存储方法地址jmp ecx调用方法
在运行时,objc_msgSend 函数会根据 isa 指针找到要调用的方法。如果方法是 静态派发(static dispatch)的,则直接调用该方法。如果方法是 动态派发(dynamic dispatch)的,则需要根据对象的类型查找要调用的具体实现。
静态派发 是指在编译时就已经确定要调用的方法。例如,调用结构体或枚举类型的方法就是静态派发的。因为结构体和枚举类型的值类型,其类型在编译时就已经确定了。
动态派发 是指在运行时才确定要调用的方法。例如,调用类的方法就是动态派发的。因为类是引用类型,其类型的具体实现可能在运行时才知道。
Swift 中的动态派发是通过 虚函数表(virtual table)实现的。虚函数表是一个包含方法地址的表,每个类都有一个虚函数表。当调用一个类的方法时,objc_msgSend 函数会根据 isa 指针找到该类的虚函数表,然后根据方法选择器找到要调用的方法地址。
虚函数表使得 Swift 具有很强的灵活性,例如可以实现多态性。多态性是指同一方法可以根据不同的对象类型表现出不同的行为。例如,我们可以定义一个 Shape 类,并定义 draw() 方法。然后,我们可以创建 Circle、Rectangle 等子类,并重写 draw() 方法。这样,当调用 draw() 方法时,会根据实际调用该方法的对象类型调用不同的实现。
以下是 Swift 中消息调用的一个示例:
Swift
class Person {
func sayHello() {
print("Hello, world!")
}
}
class Student: Person {
override func sayHello() {
print("Hello, world! I am a student.")
}
}
let person = Person()
person.sayHello() // 输出:Hello, world!
let student = Student()
student.sayHello() // 输出:Hello, world! I am a student.
请谨慎使用代码。
content_copy
在这个示例中,Person 类和 Student 类都定义了 sayHello() 方法。但是,Student 类重写了 sayHello() 方法。因此,当调用 person.sayHello() 时,会调用 Person 类的 sayHello() 实现。而当调用 student.sayHello() 时,会调用 Student 类的 sayHello() 实现。
总而言之,Swift 中的消息调用是通过运行时和虚函数表实现的。这使得 Swift 具有很强的灵活性,例如可以实现多态性。