多态:父类指针指向子类对象
多态的实现原理:
OC: Runtime
C++: 虚表(虚函数表)
Swift:类似于虚表
多态代码:
class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
}
var anim: Animal
anim = Animal()
anim.speak()
anim.eat()
anim.sleep()
anim = Dog()
anim.speak()
anim.eat()
anim.sleep()
输出结果:
对比结构体和类对方法的调用
- 结构体代码:
struct Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
var anim = Animal()
anim.speak()
anim.eat()
anim.sleep()
结构体的反汇编代码:
由上图可以看出,结构体的函数的地址在调用时一开始就固定的也就是一编译完就能确定调用哪个函数,这是因为结构体不存在继承,可以直接确定
- 类代码:
class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
var anim = Animal()
anim.speak()
anim.eat()
anim.sleep()
类的反汇编代码:
上图以此为speak,eat,sleep方法的反汇编代码,我们可以看出,它们的地址是不固定的,这是因为类存在继承特性,需要运行时才能确定真正调用哪个方法
多态实现原理
- 多态代码
class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
func run() {
print("Dog run")
}
}
var anim = Animal()
anim.speak()
anim.eat()
anim.sleep()
anim = Dog()
anim.speak()
anim.eat()
anim.sleep()
- 多态部分反汇编代码
1中是从全局变量anim的取出8个字节给rax,rax里也就是堆空间Dog对象的地址值
2中是从堆空间里取出8个字节给rcx,rcx里存储的就是Dog对象堆空间内存的前八个字节,也就是类型信息(存储着类的相关信息,包括里面的方法等)。
3就是找到存储类型信息的内存地址,加上方法的偏移量,就可以找到对应要调用的方法
图解:
注意:两个不同的Dog对象前8个字节相同,因为它存储着共同的一样的类型信息,而且类型信息存储在全局区