作者:行走少年郎
链接:https://www.jianshu.com/p/633e5d8386a8
1. 什么是 Runtime?
- OC是一门动态语言,在编译阶段并不知道变量的具体数据类型,也不知道真正的调用函数。runtime运行库就是实现OC运行时的基础。这个库使我们可以在程序运行时动态的创建对象、检查对象,修改类和对象的方法。
2. 消息机制的基本原理
- 对象方法调用都是类似
[receiver selector];
的形式。 - 编译阶段:
[receiver selector];
方法被编译器转换为:
objc_msgSend(receiver,selector)
(不带参数)
objc_msgSend(recevier,selector,org1,org2,…)
(带参数) - 运行时阶段:消息接受者 recevier 寻找对应的 selector。
1 通过recevier的isa指针找到recevier的Class(类)
2 在Class(类)的cache(方法缓存)的列表中寻找对应的IMP(方法实现)
3 如果在 cache(方法缓存) 中没有找到对应的 IMP(方法实现) 的话,就继续在 Class(类) 的 method list(方法列表) 中找对应的 selector,如果找到,填充到 cache(方法缓存) 中,并返回 selector
4 如果在 Class(类) 中没有找到这个 selector,就继续在它的 superClass(父类)中寻找
5 一旦找到对应的 selector,直接执行 recevier 对应 selector 方法实现的 IMP(方法实现)
6 若找不到对应的 selector,消息被转发或者临时向 recevier 添加这个 selector 对应的实现方法,否则就会发生崩溃
3. Runtime 中的概念解析
4. Runtime 消息转发
4.1 消息动态解析
4.2 消息接受者重定向
4.3 消息重定向
5. 消息发送以及转发机制总结
调用 [receiver selector]; 后,进行的流程:
编译阶段:[receiver selector]; 方法被编译器转换为:
objc_msgSend(receiver,selector) (不带参数)
objc_msgSend(recevier,selector,org1,org2,…)(带参数)
运行时阶段:消息接受者 recevier 寻找对应的 selector。
通过 recevier 的 isa 指针 找到 recevier 的 class(类);
在 Class(类) 的 cache(方法缓存) 的散列表中寻找对应的 IMP(方法实现);
如果在 cache(方法缓存) 中没有找到对应的 IMP(方法实现) 的话,就继续在 Class(类) 的 method list(方法列表) 中找对应的 selector,如果找到,填充到 cache(方法缓存) 中,并返回 selector;
如果在 class(类) 中没有找到这个 selector,就继续在它的 superclass(父类)中寻找;
一旦找到对应的 selector,直接执行 recevier 对应 selector 方法实现的 IMP(方法实现)。
若找不到对应的 selector,Runtime 系统进入消息转发机制。
运行时消息转发阶段:
动态解析:通过重写 +resolveInstanceMethod: 或者 +resolveClassMethod:方法,利用 class_addMethod方法添加其他函数实现;
消息接受者重定向:如果上一步添加其他函数实现,可在当前对象中利用 forwardingTargetForSelector: 方法将消息的接受者转发给其他对象;
消息重定向:如果上一步没有返回值为 nil,则利用 methodSignatureForSelector:方法获取函数的参数和返回值类型。
如果 methodSignatureForSelector: 返回了一个 NSMethodSignature 对象(函数签名),Runtime 系统就会创建一个 NSInvocation 对象,并通过 forwardInvocation: 消息通知当前对象,给予此次消息发送最后一次寻找 IMP 的机会。
如果 methodSignatureForSelector: 返回 nil。则 Runtime 系统会发出 doesNotRecognizeSelector: 消息,程序也就崩溃了。