runtime的消息发送和转发机制

2020-09-10  本文已影响0人  huicuihui

runtime的消息发送和转发机制

objc_msgSend

在OC中,所有的消息调用最后都会通过 objc_msgSend 方法进行访问。通过 objc_msgSend 进行消息调用,为了加快执行速度,这个方法在runtime源码中是用汇编实现的。

调用方法的时候底层会objc_msgSend查找方法的imp

发送消息的是一个对象。如果要找类对象需要通过对象的isa指针查找到类对象。

isa是一个isa_t公用体,里面有很多字段。

方法有方法缓存

如果缓存没有 则进行方法列表的遍历

方法缓存存在cache_t中,cache_t存在类对象objc_class结构体中。

在objc_class结构体中根据偏移计算相应结构体指针。

cache_t是一个结构体 里面有一个存储方法缓存的bucket_t数组,bucket_t里面有_imp,_sel。

根据sel和imp在方法缓存中如果没有找到,则来到__objc_msgSend_uncached方法中进行没有方法缓存的查找。

总结

objc_msgSend在查找的时候先进行一个缓存的命中,通过地址的与运算,拿到当前的缓存,和当前要查找的sel进行比对,如果比对上就命中了,直接使用。没有比对上则进行__objc_msgSend_uncached。

__objc_msgSend_uncached

没有查找到缓存的时候,需要遍历方法列表。lookUpImpOrForward。

lookUpImpOrForward

调用 lookUpImpOrForward 方法,返回值是个 IMP 指针,如果查找到了调用函数的 IMP ,则进行方法的访问

  1. 当前类对象的方法列表中遍历方法列表。
  2. 沿着当前继承链当中的superClass,指针的指向,来进行方法遍历查找。
  3. 一直遍历到Root Class(NSObject)

消息转发机制 动态特性

如果没有查到对于方法的 IMP 指针,则进行消息转发机制

_objc_msgForward消息转发做的几件事

  1. 第一层转发:(动态方法解析)

    调用 resolveInstanceMethod:resolveClassMethod:,允许用户在此时为该Class动态添加实现。如果实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。本质上是在方法列表中建立SEL和新的IMP关系。新的IMP是自己做的。如果仍没实现,继续下面的动作。

  2. 第二层转发:(标准消息转发,obj_msgForward 方法的转发)找一个备用的接收者来处理消息。

    如果第一层转发返回 NO ,则会进行第二层转发,调用forwardingTargetForSelector:,可以把调用转发到另一个对象,这是类级别的转发,调用另一个类的相同的方法。

    注意:这里不要返回self,否则会形成死循环。

  3. 第三层转发:(标准消息转发,obj_msgForward 方法的转发)爱咋咋地,消息拦截了,自己处理。

    如果第二层转发返回 nil ,则会进入这一层处理

    这层会调用 methodSignatureForSelector:尝试获得一个方法签名。如果获取不到则直接调用doesNotRecognizeSelector:抛出异常。如果能获取到,则返回非nil,创建一个NSInvocation并传给forwardInvocation:

    调用forwardInvocation:方法,将上面获取到的方法签名包装成Invocation传入,如何处理就在这里面了,并返回nil。

    这次是完整的消息转发,因为你可以返回方法签名、动态指定调用方法的Target

  4. 如果转发都失败,调用doesNotRecognizeSelector:,crash抛出异常。

_objc_msgForward在进行消息转发的过程中会涉及以下几个方法:

  1. resolveInstanceMethod:(resolveClassMethod:)
  2. forwardingTargetForSelector:
  3. methodSignatureForSelector:
  4. forwardInvocation:
  5. doesNotRecognizeSelector:

一旦调用_objc_msgForward将会跳过查找IMP的过程,直接触发消息转发。

上一篇下一篇

猜你喜欢

热点阅读