前言
Object-C是一门动态语言,Rumtime更是OC动态特性中最重要的一部分,今天我们就来深入了解一下 Runtime中的消息传递机制
首先我们要区分两个概念,编译时,运行时
编译时:编译时只是对语言进行最基本的检查报错,包括词法分析、语法分析等等 我们日常开发中用的 OC 、 Swift、 Java 等等都是高级语言,(高级语言 可阅读性强)但是不被计算机识别,所以编译就可以把高级语言编译成计算机可识别的汇编语言 系统所识别的二进制 ,但是编译成功 不代表可以运行。
运行时:运行时即程序通过了编译之后编译好的代码被装载到内存中跑起来的阶段,这个时候会具体对类型进行检查,而不仅仅是对代码的简单扫描分析,此时若出错程序会崩溃。
现在我创建了一个TestClass的类 定义了一个名为run的实例方法没有在.M中实现 。然后在main函数中调用,很显然在编译时没有没有报错。
#import <Foundation/Foundation.h>
@interface TestClass : NSObject
- (void)run;
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
TestClass *class =[[TestClass alloc]init];
[class run];
}
}
虽然没有对应run方法的实现,但是编译通过了,并没有爆出错误,从而证明OC的动态特性, 方法的实际调用是在运行时进行的,通过动态绑定机制来决定需要调用的方法。
objc_msgSend()函数
void objc_msgSend(id self, SEL cmd, ...)
在OC中调用方法 我们可以看做就是给某个对象发送消息 而objc_msgSend()函数就是其中最核心的发送消息函数
这是一个参数个数可变的函数。能接收两个或两个以上的参数,第一个参数代表接受者,第二个参数代表方法名。后续参数就是消息中的那些参数,其顺序不变。接受者就是调用方法的对象或者类(本质上类也是对象,叫做类对象)。运行时,上面Objc的方法调用会被翻译成一条C语言的函数调用,如下:
objc_msgSend(class, @selector(run))
消息传递流程
当调用objc_msgSend()函数会经历以下过程
1.从缓存中查找对应SEL(封装后方法名)的IMP (指向函数实现的指针)
被调用过的方法会存在缓存里面,每个类都会有一个表来存被调用过的方法,以便下次更快的调用。(这个是比较快速的)
2.从方法列表中查找
接受者会通过isa 找到自己的元类(metaClass),从元类的方法列表继续查找对应SEL(方法名)的IMP 如果找到则写入缓存,调用。如果没有找到再从父类中查找方法,如此往复,直到达到基类。如果找不到则执行方法的动态解析。
3.消息转发机制--动态解析
调用
+ (BOOL)resolveInstanceMethod:(SEL)sel
方法来查看是否能够返回一个selector,如果存在则返回selector。不存在进入下一步。
4.备用接受者
- (id)forwardingTargetForSelector:(SEL)aSelector
这个方法来询问是否有接受者可以接受这个方法呀。如果有人接受,则交给它处理,就好像一切都没发生过一样。
5.完整转发机制--消息重定向
如果到这一步还不能够找到相应的Selector的话,就要进行完整的方法转发过程。调用方法
-(void)forwardInvocation:(NSInvocation *)anInvocation
最后还是没有找到的话就会爆出unrecognized selector sent to instance 0x100111df0'的错误就来了。
image.png以上就是消息传递机制的全部流程
至于第三步消息转发机制的详细流程会在之后的文章详细说明 最后放上一张关于元类(metaClass)的详细关系图
网友评论