11.iOS底层学习之消息转发

上一篇学习了消息查找的动态方法决议,这一篇主要来探讨如果开发者没有实现动态方法决议提供的两个方法,那么消息流程将会继续怎样执行。
这篇文章主要围绕着两个问题进行展开:
1、动态方法决议之后的流程是怎样引入的;
2、动态方法决议之后的具体流程是怎么样的;

动态方法决议之后的流程是怎样引入的

我们依旧回归源码,来到方法lookupImpOrForward动态方法决议完毕的部分,来看一下后面的代码怎么执行:

    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
 done:
    if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
        while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
            cls = cls->cache.preoptFallbackClass();
        }
#endif
        log_and_fill_cache(cls, imp, sel, inst, curClass);
    }
 done_unlock:
    runtimeLock.unlock();
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;

我们根据上面的代码了解在执行完毕resolveMethod_locked流程后,依旧没有实现动态方法决议提供的补救方法,也就是依旧查不到方法,此时返回的resolveMethod_locked是个nil,所以从lookupImpOrForward去查看后续流程的思路就断了。
此时我们借助查看系统日志的方式来了解下到底发生了什么事情,所以引入了一个方法instrumentObjcMessageSends

instrumentObjcMessageSends介绍

作用:打印OC内部调用了哪些方法。
使用:1、声明外部函数 2、设置起点和终点。3、查看打印日志到/private/tmp/文件夹下,会发现msgSends-xxxx文件,打开查看。
我们通过这个方法来看一下系统具体的方法调用:


image.png

我这边生成了文件msgSends-83990:

image.png

可以看到在没有实现方法relsolveInstanceMethod和relsolveClassMethod之后系统调用了方法:forwardingTargetForSelector、methodSignatureForSelector、resolveInstanceMethod、doesNotRecognizeSelector。所以我们通过关键方法instrumentObjcMessageSends的系统日志打印来了解和引入查看系统的后续流程。

动态方法决议之后的具体流程是怎么样的

通过上面的学习,了解到动态方法之后的流程是forwardingTargetForSelector-> methodSignatureForSelector-> doesNotRecognizeSelector。也就是进入到了我们常说的消息转发流程

forwardingTargetForSelector

通过官方文档可以了解到forwardingTargetForSelector的作用是:

Discussion

If an object implements (or inherits) this method, and returns a non-nil (and non-self) result, that returned object is used as the new receiver object and the message dispatch resumes to that new object. (Obviously if you return self from this method, the code would just fall into an infinite loop.)

If you implement this method in a non-root class, if your class has nothing to return for the given selector then you should return the result of invoking super’s implementation.

This method gives an object a chance to redirect an unknown message sent to it before the much more expensive forward<wbr data-v-78dd96cf="">Invocation:machinery takes over. This is useful when you simply want to redirect messages to another object and can be an order of magnitude faster than regular forwarding. It is not useful where the goal of the forwarding is to capture the NSInvocation, or manipulate the arguments or return value during the forwarding.

直译一下:如果一个对象实现或者继承了这个方法,并且返回了空结果的时候,那么这个被返回的对象会被使用作为一个新的接受者,消息会重新派发给新的对象。(显然如果你返回self,那么代码将会进入无止境的循环。)
如果你实现了这个方法在不是基类中,如果你的类没有返回为这个对应的方法那么你应该返回调用父类的实现结果。
这个方法给了对象一次在执行非常珍贵的方法forward<wbr data-v-78dd96cf="">Invocation:之前一次重定向一个未知消息发送的机会。这会非常有作用当你想重定向消息给另外一个对象并且会比常规的消息发送快一个数量级。它不适用于当目的是捕获NSInvocation,或者在发送过程中修改参数或者返回值。

methodSignatureForSelector

同样了解一下官方文档给的描述:

Discussion
This method is used in the implementation of protocols. This method is also used in situations where an NSInvocation object must be created, such as during message forwarding. If your object maintains a delegate or is capable of handling messages that it does not directly implement, you should override this method to return an appropriate method signature.

直译一下:这个方法是用于协议的实现。这个方法也适用在场景当NSInvocation对象必须被创建,例如在消息发送。如果你的对象成为了代理或者可以持有消息那么它不直接处理实现,你应该重写这个方法并且返回一个合适的方法签名。

这个方法要结合方法** forwardInvocation**来看,因为在学习methodSignatureForSelector时官方文档下面提到了相关方法就是forwardInvocation,而且上面的描述也提到methodSignatureForSelector不能直接去处理消息,只是生成一个对象并且成为消息的代理,我理解这个地方的最终实现是由forwardInvocation方法完成的。所以我们来看一下forwardInvocation。

forwardInvocation

关于方法forwardInvocation文档的描述有点长,这个方法有两个任务:
-定位可以响应通过anInvocation编码的对象。对于所有的消息,这个对象不必相同。
-发送消息给使用anInvocation的对象。一个anInvocation会保留结果,并且运行时会取出并且转发这个消息给原始发件人。

doesNotRecognizeSelector

Discussion
The runtime system invokes this method whenever an object receives an aSelector message it can’t respond to or forward. This method, in turn, raises an NSInvalidArgumentException, and generates an error message.

运行时会抛出这个方法当一个对象接受到一个无法被响应或者发送的消息。这个方法,在执行时,会采集一个叫NSInvalidArgumentException的异常并且创造一个错误消息。

通过以上的流程,我们来总结一下动态方法决议之后的过程,也就是消息转发:
-先来到方法forwardingTargetForSelector,这个方法执行消息非常快速,并且如果不是在根类里面实现的话需要调用父类,不能返回自己,否则进入死循环。
-如果forwardingTargetForSelector没有进行方法重定向那么会来到方法methodSignatureForSelector,也就是方法签名,这个方法会返回一个NSMethodSignature对象,该对象遵守了NSInvocation的协议。该方法不负责具体的实现,最终的实现由协议方法forwardInvocation来完成。所以methodSignatureForSelector和forwardInvocation是成对出现的。
-如果以上都没有实现,那么会来到方法doesNotRecognizeSelector会发送一个错误,并且采集当前的报错信息。
以上为整个消息转发的流程。消息发送以及消息转发就完整了!

-------------------------------神奇的分割线-----------------------------------------------
最后附一张自己复习重新画了一遍整个消息部分的流程图,有问题欢迎提出我好修改👏🏻


objc_msgSend.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容