Swift方法派发,动态特性
2021-07-30 本文已影响0人
lieon
Swift 动态特性
- 所谓动态:就是在运行阶段才知道自己的类是什么
- 父类对象指向子类对象,是动态特性,因为在运行的时候,才知道这个变量真正的类型是子类
- 具有方法动态派发(函数表派发,消息派发)
- 继承自OC的类Swift类,具有动态特性
-
dynamic
修饰的方法和属性,具有动态特性 - Swift中的反射机制,是动态特性
- 自省:比如使用
is
关键字来判断类型是否相同 - SWift对于继承自OC的类,可以使用KVC,KVO,关联对象等等
静态派发
- 在编译期的时候,编译器就知道要为某个方法调用某种实现
动态派发
- 对于每个方法的调用,编译器必须在方法列表中查找执行方法的实现,比如在运行时判断是选择父类的实现,还是子类的实现。对于对象的内存都是在运行时分配的,因此只能在运行时执行检查
编译型语言的函数派发方式
- 直接派发(Direct Dispatch)
- 编译后确定了方法的调用地址(静态派发),汇编代码中,直接跳到了方法的地址执行,生成的汇编指令最少,速度最快
- 例如C语言,C++默认也是直接派发
- 由于缺乏动态性,无法实现多态
- 函数表派发(Table Dispatch)
- 在运行时通过函数查找需要执行的方法,多一次查表的过程,速度比直接派发慢
- C++的虚函数(Virtual Table),维护一个虚函数表,对象创建的时候会保存虚表的指针,调用方之前,会从对象中取出虚表地址,根据编译时的方法偏移量从虚表取出方法的地址,跳到方法的地址执行
- 消息派发(Message Dispatch)
- objc消息发送机制
- 性能:直接派发 > 函数表派发 > 消息机制派发
直接派发
- 在Swift中,下面方法会被编译为直接派发
全局函数
- 使用
static
声明的方法 - 使用
final
声明的所有方法,使用final
声明的类里面的所有方法 - 使用
private
声明的方法和属性,会隐式final
声明 -
值类型
的方法,比如struct
和enum
中的方法 - 在
extension
中没有使用@objc
修饰的实例方法
函数表派发
- 只有引用类型材质函数表派发
- 在Swift中,类的方法默认使用函数表派发的方式
- Swift对于协议
Protocol
默认使用的是函数表派发,协议可以为struct提供多态的支持 - Swift的函数表叫做
witness table
(C++叫做virtual table)- 每个子类都有自己的表结构
- 对于类中每个重写的方法,都有不同的函数指针
- 当子类添加新方法时,这些方法指针会添加到函数表的末尾
- 在运行时使用此表来调用函数的实现 (函数表指针 + 函数偏移 就能找到对应的方法)
消息派发
- 用
dynamic
修饰的方法是消息派发 -
注意:
@objc
修饰方法,只是把方法暴露给objc
对于继承自NSObject的Swift类,方法派发方式
- 普通的实例方法,使用函数表派发
-
@objc
声明的方法,使用函数表派发 - 对于重写了OC的方法。使用消息派发
-
extension
中的方法,直接派发 -
dynamic
修饰的方法,使用消息派发
// 直接派发
func all() { }
class Person {
final var age: Int {
return 10
}
func eat() {
}
func commonMethod() {
}
/// private修饰:直接派发
private func jump() { }
static func staticMethod() {}
final func finalMethod() { }
}
extension Person {
func extensionMethod() { }
}
struct Model {
func modelFunc() { }
}
final class Student: Person {
override func eat() {
}
func run() {}
}
class Graduates: Person {
func study() {
}
}
class Animal {
dynamic func run() {
}
}
/// 协议采用的函数表派发的方式
protocol Eateable {
func swite()
}
class Apple: Eateable {
func swite() {
}
}
class CustomView: UIView {
/// static修饰:直接派发
static func staticMehod() {}
/// private: 直接派发
private func privateMethod() {}
/// final修饰:直接派发
final func finalMethod() {}
/// 直接派发
static func staticMethod() {}
/// 普通的实例方法 函数表派发
func commonMethod() {}
/// @objc修饰 函数表派发
@objc func method1() {}
/// dynamic修饰: 消息派发
@objc dynamic
func method2() {}
/// 重写了OC的方法: 消息派发
override func layoutSubviews() {
super.layoutSubviews()
}
}
class Test {
func handle() {
let person = Person()
/// 全局函数直接派发
all()
/// 引用的类型的实例方法: 函数表派发
person.eat()
person.commonMethod()
let graduate = Graduates()
graduate.study()
/// final修饰:直接派发
person.finalMethod()
/// static方法 直接派发
Person.staticMethod()
/// extension中没有被@objc修饰:直接派发
person.extensionMethod()
/// student final类型;所有方法的均为直接派发
/// final 关键字可以用在 class,func 或者 var 前面进行修饰,表示不允许对该内容进行继承或者重写操作
let student = Student()
student.eat()
student.run()
/// 值类型:直接派发
let data = Model()
data.modelFunc()
}
}