OC方法调用(消息机制)
OC的方法调用:消息机制
给方法调用者发送消息
objc_msgSend(id, SEL, ...)
OC中的方法调用,其实都是转换成objc_msgSend函数调用
主要分为
1.消息发送
2.动态方法解析
3.消息转发
如果三个流程都无法找到则会报一个非常经典的错误
unrecognized selector sent to instanceobjc_msgSend 执行流程
流程一 消息发送
判断receiver(方法接受者) 是否为空 不为空继续 为空就退出当前的消息发送流程
从receiverClass(方法接受者当前类)的方法chache(缓存)中找 找到退出 找不到继续
从receiverClass的 class_rw_t 的方法列表中查找 成功就将方法缓存到receiverClass的cache中 否则继续
从superClass(父类)的cache中查找 成功就将方法缓存到receiverClass的cache中 否则继续
从superClass(父类)的class_rw_t中查找 成功就将方法缓存到receiverClass的cache中 否则继续
superClass如果没有就继续向上层的 superClass中查找 一直循环4,5,6步骤 直至找到为止
如果还是没有找到就会进入消息机制的第二个阶段 动态方法解析
注:
1.class_rw_t 查找的方式 如果已经排序的 二分查找 没有排序的遍历查找
2.receiver 通过isa指针找到receiverClass
3.receiverClass 通过superclass指针找到superClass
流程二 动态方法解析
动态解析有一个BOOL值的标记 如果已经执行过一次 就不会再执行
不管是否手动添加方法实现 如果是第一次 都会返回流程一重新找一次
此时你已经手动添加方法实现了方法 所以流程一(从流程一的第二步开始执行)一定会执行成功 如果这里也没有手动添加方法实现的话 同样会走到流程一 但是流程一同样不会成功执行 然后检测流程二执行过就会跳到 第三个阶段消息转发
(BOOL)resolveInstanceMethod:(SEL)name;//添加实例方法
+ (BOOL)resolveClassMethod:(SEL)name;//添加类方法
当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。
当你给类发消息时,消息是在寻找这个类的元类的方法列表。这两个方法的调用取决于对象调用的是实例方法还是类方法


流程三 消息转发

类方法没有消息转发机制是错的 其实他是有的
⽅法的本质?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)
通过⻚码去定位具体的内容