runtime
概述: runtime又叫运行时,是一套底层C语言API,是iOS系统的核心之一。开发者在编码过程中,可以给任意一个对象发送消息,在编译阶段只是确定了要向接受着发送这条消息,而接受者如何响应和处理这条消息,就要看运行时来决定了
C语言中,在编译器就确定要调用哪个函数,而OC的函数,属于动态调用过程,在编译器并不能真正决定调用哪个函数,只有在真正的运行时才会根据函数的名称找到对应的函数来调用。OC是一个动态语言,这意味着它不仅要一个编译器,也需要一个运行时系统来动态创建类和对象、进行消息传递和发送
1.消息转发
Runtime的特性主要是消息传递,如果消息在对象中找不到,就进行转发。Objective-C是一个动态语言,这意味着它不仅需要一个编译器,也需要一个运行时系统来动态创建类和对象、进行消息传递和转发。Runtime的核心是消息传递。
(1)消息传递的过程
一个对象的方法[obj test],编译器转成消息发送objc_msgSend(obj,test),Runtime执行的流程是这样的
a.首先通过obj的isa指针找到它的class
b.在class的method list找test
c.如果class中没找到test,继续往它的superclass中找
d.一旦找到test这个函数,就去执行它的IMP
由于效率问题,每个消息都遍历一次objc_method_list并不合理,所以需要把经常被调用的函数缓存下来,去提高函数查询的效率。这也就是objc_class中另一个重要的成员objc_cache做的事情。找到test之后,将test的method_name作为key,method_imp作为value。当再次收到test消息的时候,可以直接在cache里找。
类对象(objc_class)
Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。struct objc_class结构体里面定义了很多变量。结构体里保存了指向父类的指针、类的名字(name)、版本、实例变量列表(ivars)、方法列表(methodLists)、缓存(cache)、遵守的协议列表(protocols),由此可见,类对象就是一个结构体struct objc_class,这个结构体存放的数据就是元数据
理解Runtime就是理解iOS在运行时他的数据存储以及他的类、实例、类对象、元类她们之间的关系和作用。
(2)消息转发机制
归根到底,Objective-C中所有的方法调用本质就是向对象发送消息
1.类中创建方法-(void)test
2.iOS系统为这个方法创建一个编号,SEL(test)并添加到方法列表里面
3.当调用这个方法的时候系统去方法列表里查找这个方法,找到了就执行
所以,调用一个方法就会进行一次发送消息也就是在这个类的方法列表里找,如果在该类中找不到就到该类的父类里找,如果父类还找不到就一直搜索到继承树的根部,如果找不到或者消息转发不成功那就会报unrecognized selector错。
1.动态方法解析
Objective-C运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:让你有机会提供一个函数实现,如果你添加了函数并且返回YES,那么运行时就会重新启动一次消息发送的过程。如下图:
虽然没有foo:的实现函数,但是通过class_addMethod()动态添加了fooMethod函数,并执行了这个函数并且打印成功。如果reslove返回NO运行时就会移到下一步:forwardingTargetSelector
2.直接消息转发
如果目标对象实现了forwardingTargetSelector,Runtime这时就会调用这个方法,给你把这个消息转发给其他对象的机会
从图中可以看出我们通过forwardingTargetForSelector方法将当前类的方法转给Father类实现了,打印成功。
3.完整消息转发
如果在上一步还不能处理未知消息,那唯一能做的就是启动消息转发机制。首先它会发送methodSignatureForSelector消息获得函数的参数和返回值类型。如果methodSignatureForSelector返回nil,Runtime则会发出doesNotRecognizeSelector。如果返回一个签名函数,Runtime就会创建一个NSInvocation对象并发送forwardInvocation消息给目标对象。
Runtime的实际应用
1.使用Runtime交换方法
2.动态添加方法(目前不是很懂)
3.给分类添加属性
4.消息转发(热更新)解决Bug(JSPatch)