OC运行时
- 动态类型
- 动态绑定
- 动态装载
OC的消息转发机制
分为两步
- 实例变量会先查找自身有没有该方法,没有就再去父类,直到最上级父类也没有,就会转向第二步
- 这一步会有很多个地方让用户重启消息发送过程
- resolveInstanceMethod
- forwardingTargetForSelector
- methodSignatureForSelector -> forwardInvocation
- doesNotRecognizeSelector
派发方式
- 直接派发(Direct Dispatch)
- 最快,需要调用的指令集会更少,编译器还能够优化,也成为静态调用,但是缺乏动态性所有没办法支持继承和多态
- 函数表派发(Table Dispatch)
- 使用一个数组存储类声明的每一个函数的指针,也成为virtual table(虚函数表),Swift里成为witness table.
- 每个类都会维护一个函数表,记录类所有的函数,如果父类函数被override,则只会保存被override之后的函数,一个子类新添加的函数,都会被插入到数组的最后
- 比直接派发慢,不好扩展
- 消息机制派发(Message Dispatch)
- 调用函数最动态的方式,Cocoa的基石,支持函数替换、kvo这些功能
Swift运行时
- 纯Swift类的函数不再是运行时发消息,而是类似 C++ 的 vtable,在编译时就确定了调用哪个函数,所以没法通过 runtime 获取方法、属性
- 为了兼容OC,凡是继承自NSObject的类都会保留其动态性,能通过 runtime 拿到它的方法。新版本必须手动加上@objc
- 不管是纯 Swift 类还是继承自 NSObject 的类只要在属性和方法前面加上 @objc 关键字就可以使用 runtime
下面是一些 Swift 运行时的派发方式
原始定义 | 扩展 | |
---|---|---|
值类型 | 直接派发 | 直接派发 |
协议 | 函数表派发 | 直接派发 |
类 | 函数表派发 | 直接派发 |
继承自NSObject的类 | 函数表派发 | 消息机制派发 |
final | 直接派发 |
dynamic | 消息机制派发 |
@objc & @nonobic | 改变在OC里的可见性 |
@inline | 告诉编译器可以直接派发 |