OC runtime_1(消息传递,消息转发)

2015-11-05  本文已影响130人  钱嘘嘘

运行时(runtime)系统是一个提供一系列公开函数接口以及数据结构的动态链接库

消息传递机制:

OC是动态语言,所有方法的调用都会通过runtime动态地转换成消息发送,这个过程中就用到了相应的运行时方法:

receiver:接受者,selector:本质是方法名

(1) 在传递给接受者(实例对象)的过程中,需要通过对象的isa指针寻找“对象所属的类对象”,在这之前,引入了“OC对象”的内存布局:

OC对象包含一个isa指针,指向其所属的类对象,同时包含其所有父类的实例变量。



(2) 接着,在其父类对象的方法列表中寻找方法以供调用,这里又引出了“类对象”中存放的内容:

                                   <1>  实例对象的方法列表(方法名称,IMP实现,参数类型)

                                   <2>  成员变量列表

                                   <3>  属性列表


(3) 然后,因为类对象也是对象,所有也有isa指针,指向元对象。引入“元类对象”中存放的内容:

                                    <1>  类方法列表 (方法名称,IMP实现,参数类型)

                                    <2>  superclass指针


(4) 实例对象,类对象,元对象通过isa指针关联,类对象和元对象通过superclass指针与父类关联,从而形成如下图一样的闭环:

PS:   <1> 根类是NSObject,它的superclass指针指向nil。

          <2> 所有元类对象的isa指针都指向根元类,根元类  isa指针指向自己,superclass指针指向NSObject。


(5) 接着(2)中,找完父类的方法列表,没找到,会继续找父类的父类,一直找到顶层的父类,如果没有找到会抛出“unrecognized selector sent to XXX”异常。在这之前,runtime提供了三次拯救程序崩溃的机会:

           <1> Method resolution

      你可以通过实现 +resolveInstanceMethod: 以及 +resolveClassMethod: 方法以动态的实现一个指定实例的实例方法或者类方法。

PS: 一般说来,消息转发机制 和 动态加载机制 是正交的。在转发机制介入之前,一个类有机会动态的加载一个方法。

            <2> Fast forwarding

       如果对象实现 -forwardingTargetForSelector:,runtime就会调用这个方法,给把这个消息转发给其他对象的机会。

            <3> Normal forwarding

        运行时发送 -methodSignatureForSelector: 消息获得函数参数和返回值类型,如果返回nil,运行时会发送 -doesNotRecognizeSelector消息,然后抛出(5)中异常。如果返回了函数签名,运行时就会创建一个NSInvocation对象并发送 -forwardInvocation: 消息给目标对象。

PS:

        为了加速上述消息调用的过程。运行时系统会缓存它们使用过的方法名以及对应的方法地址。这些缓存是针对每个类单独缓存的,当然,这其中包含它自己定义的方法以及继承自父类的方法。在搜索方法列表之前,消息传递路由会先检查接收消息接收类的缓存(基于如果干过一次就很可能还会干下一次的理论)。如果方法名存在缓存中,消息传递只比直接的函数调用慢那么一点点。一旦一个程序已经跑了足够长的时间以“预热”他的缓存,几乎所有发送的消息就都可能被缓存了。缓存是根据程序运行时新消息的调用动态增长的。

引文:

        OC运行时编程指南

      《招聘一个靠谱的iOS》面试题参考答案

上一篇下一篇

猜你喜欢

热点阅读