OC的方法调用:消息机制

给方法调用者发送消息

objc_msgSend(id, SEL, ...)

OC中的方法调用,其实都是转换成objc_msgSend函数调用

主要分为

1.消息发送

2.动态方法解析

3.消息转发

如果三个流程都无法找到则会报一个非常经典的错误

unrecognized selector sent to instance

objc_msgSend 执行流程

流程一 消息发送

  1. 判断receiver(方法接受者) 是否为空 不为空继续 为空就退出当前的消息发送流程

  2. 从receiverClass(方法接受者当前类)的方法chache(缓存)中找 找到退出 找不到继续

  3. 从receiverClass的 class_rw_t 的方法列表中查找 成功就将方法缓存到receiverClass的cache中 否则继续

  4. 从superClass(父类)的cache中查找 成功就将方法缓存到receiverClass的cache中 否则继续

  5. 从superClass(父类)的class_rw_t中查找 成功就将方法缓存到receiverClass的cache中 否则继续

  6. superClass如果没有就继续向上层的 superClass中查找 一直循环4,5,6步骤 直至找到为止

  7. 如果还是没有找到就会进入消息机制的第二个阶段 动态方法解析

注:

1.class_rw_t 查找的方式 如果已经排序的 二分查找 没有排序的遍历查找

2.receiver 通过isa指针找到receiverClass

3.receiverClass 通过superclass指针找到superClass

流程二 动态方法解析

  1. 动态解析有一个BOOL值的标记 如果已经执行过一次 就不会再执行

  2. 不管是否手动添加方法实现 如果是第一次 都会返回流程一重新找一次

  3. 此时你已经手动添加方法实现了方法 所以流程一(从流程一的第二步开始执行)一定会执行成功 如果这里也没有手动添加方法实现的话 同样会走到流程一 但是流程一同样不会成功执行 然后检测流程二执行过就会跳到 第三个阶段消息转发

(BOOL)resolveInstanceMethod:(SEL)name;//添加实例方法

+ (BOOL)resolveClassMethod:(SEL)name;//添加类方法

当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。

当你给类发消息时,消息是在寻找这个类的元类的方法列表。

这两个方法的调用取决于对象调用的是实例方法还是类方法

60ffb08206007.png

60ffb18e14775.png

流程三 消息转发

60ffc5127e611.png

类方法没有消息转发机制是错的 其实他是有的

⽅法的本质?sel是什么?IMP是什么?两者之间的关系⼜是什么?

方法的本质:发送消息流程

快速消息查找 (objc_msgSend),cache_t 缓存查找消息。

慢速消息查找(lookUpImpOrForward)递归自己以及父类,自己找不到去父类缓存中找,依然找不到会进行父类慢速查找,直到找到nil。

查找不到消息进行动态方法解析(resolveInstanceMethod/resolveClassMethod)。resolveClassMethod的过程中如果没有找到方法,会调用resolveInstanceMethod。

消息快速转发(forwardingTargetForSelector),相当于找消息备用接收者。

消息慢速转发(methodSignatureForSelector & forwardInvocation),在仍然没有解决问题后在methodSignatureForSelector的时候会再进行一次慢速消息查找(这次不进行消息转发)。

最后仍然没有解决问题会进入doesNotRecognizeSelector抛出异常。

sel是方法编号,在read_images 期间就编译进入了内存。

imp 就是函数实现指针 ,找imp就是找函数的过程。

可以将sel-imp理解为书本的目录,sel书本目录的名称,imp 就是书本的⻚码。查找具体的函数就是想看这本书里面具体篇章的内容。

1:我们⾸先知道想看什么 -- > title (sel)

2:根据⽬录对应的⻚码 -- >(imp)

3:翻到具体的内容

imp与SEL 的关系

SEL : ⽅法编号

IMP : 函数指针地址

SEL 相当于书本⽬录的名称

IMP : 相当于书本⽬录的⻚码

⾸先明⽩我们要找到书本的什么内容 (sel ⽬录⾥⾯的名称)

通过名称找到对应的本⻚码 (imp)

通过⻚码去定位具体的内容