2021-08-02
分类:IOS开发
评论(0)
runtime
1. 什么是runtime?
- runtime 运行时机制,是一套比较底层的纯 C 语言 API , 属于1个 C 语言库, 包含了很多底层的 C 语言 API 。(引入 <objc/runtime.h> 或者 <objc/message.h> )
- 程序运行过程时,我们平时编写的 OC 代码, 其实最终都是转成了 runtime 的 C 语言代码。
- 在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才能根据函数的名称找到对应的函数来调用。
2. runtime 是干什么用的?使用场景是什么?
- runtime 是属于 OC 的底层, 可以进行一些非常底层的操作;
- 使用场景1:动态创建一个类 ( 比如 KVO 的底层实现 ) objc_allocateClassPair、class_addIvar、objc_registerClassPair; 例如:热创建。
- 使用场景2:动态地为某个类添加属性/方法, 修改属性值/方法(修改封装的框架) objc_setAssociatedObject、object_setIvar; 例如:热更新。
- 使用场景3:遍历一个类的所有成员变量(属性)/所有方法(字典转模型,归解档) class_copyIvarList、class_copyPropertyList、class_copyMethodList;例如:YYmodel、MJextension、JsonModel。
- 使用场景4:查找对象,实现万能跳转;例如:收到推送的通知跳转到对应的页面。
消息转发机制
1. 消息转发机制的原理
- 当向一个对象发送消息时,objc_msgSend 方法根据对象的 isa 指针找到对象的类,然后在类的调度表(dispatch table)中查找 selector。
- 如果无法找到 selector,objc_msgSend 通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找 selector,以此类推直到 NSObject 类。
- 一旦查找到 selector,objc_msgSend 方法根据调度表的内存地址调用该实现。
- 通过这种方式,message 与方法的真正实现才在执行阶段进行绑定。
- 为了保证消息发送与执行的效率,系统会将全部 selector 和使用过的方法的内存地址缓存起来。
- 每个类都有一个独立的缓存,缓存包含有当前类自己的 selector 以及继承自父类的 selector。
- 查找调度表(dispatch table)前,消息发送系统首先检查 receiver 对象的缓存;缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点。
2. 消息转发中各个参数分别代表什么?
- sel: 一种类型,表示方法名称,类似字符串(可互转)。
- isa: 在方法底层对应的 objc_msgSend 调用时会根据 isa 找到对象所在的类对象,类对象中包含了调度表(dispatch table),该表将类的 sel 和方法的实际内存地址关联起来。
- super_class: 每一个类中还包含了一个 super_class 指针,用来指向父类对象。
- _cmd: 在Objective-C 的方法中表示当前方法的 selector,正如同 self 表示当前方法调用的对象实例。
- IMP: 定义为 id (*IMP) (id, SEL, …)。这样说来, IMP 是一个指向函数的指针,这个被指向的函数包括 id (\”self\”指针),调用的 SEL(方法名),再加上一些其他参数。说白了 IMP 就是实现方法。
3. 动态绑定
- 在运行时确定要调用的方法,动态绑定将调用方法的确定也推迟到运行时。
- 在编译时,方法的调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。
- 通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。
- 运行时因子负责确定消息的接收者和被调用的方法;运行时的消息分发机制为动态绑定提供支持。
- 当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者的 isa 指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在 Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。
- 您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生