OC 当中,runtime 可以说是重中之重,很多的第三方SDK,诸如: Aspects、JSPatch等,都是利用runtime 实现的。
那么runtime究竟是什么呢?
一、运行时语言
OC作为运行时语言,和其他语言在编译期就已经确定了方法的具体执行内容不同,OC是在运行期去判断执行内容。
runtime 就是在运行期查找调用方法的过程, 即 [object msgSend].
二、类与对象
作为面向对象的语言,万物皆为对象,而对象是由 类 实例化而来。
在OC当中,类的结构为:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
其中isa 指向Class 的 元类(meta-class), super_class 指向Class的父类。
其继承链为:
meta-class的isa 指针最终指向 NSObject 的meta-class.
NSObject的meta-class的isa 指针 指向自身,super_class指向NSObject.
什么是meta-class?
在结构体当中,声明了isa变量,指向meta-class.
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
一个类,可以有实例方法,也可以有类方法。所有的实例方法都存储在类的 methodLists:
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
而类方法就存储在了meta-class的methodLists当中。
当调用类方法时,会在类的isa指针所指向的meta-class的方法列表当中查找。
那么meta-class的isa指针指向哪里呢? OC的设计者让所有的meta-class的isa指针都指向了NSObject的meta-class;
也就是说: 任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。
三、消息传递机制
作为运行时语言,OC在运行时才会查找方法的具体实现,即:
objc_msgSend(receiver, selector, arg1, arg2,...)
通过这个方法来查找方法对应的实现。
1. 首先判断 Obj 实例是否为nil,若为nil 则不作任何操作返回nil;
2.若obj不为空,如果是实例方法,则在obj的cache 中查找是否存在 相应的SEL
3. 如果cache中没有,则继续在 methodLists 当中查找
4. 如果还是没有找到相应的SEL,则沿着 obj的superClass逐级查找,直至NSObject类
5. 如果在继承链中没有找到SEL,则执行消息转发机制。
如果找到了SEL方法,则根据SEL查找Method,找到对应的实现方法IMP。
四、消息转发机制
没有找到相应的实现方法时,OC就会执行消息转发,给以三次的机会来避免程序crash。
如上图所示,消息转发机制分为三个步骤
1、动态方法解析
+resolveInstanceMethod: // 对应实例方法
+resolveClassMethod: // 对应类方法
可以通过 class_addMethod()方法,动态为SEL添加实现方法,同时返回true,
2、备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector
这步可以指定某个对象作为接收者来相应消息,如果返回nil,则进入第三步;若返回不为空的对象,则将在其方法列表中查找合适的SEL 执行。
3、完整消息转发
如果前两步都没有做任何操作,系统将会把未实现的SEL及对应的class 分装成NSInvocation对象抛出。
- (void)forwardInvovation:(NSInvocation)anInvocation
可以在此步中实现方法,修改相应对象等,如果调用成功,则结束;如果没能正确相应方法,则会抛出异常,程序crash。
参考:
https://www.tuicool.com/articles/Av63EfJ
https://www.jianshu.com/p/58c985408b75
http://blog.devtang.com/2013/10/15/objective-c-object-model/