OC-底层原理09—消息转发流程
来源:     阅读:471
织梦模板店
发布于 2021-03-20 04:39
查看主页

iOS--OC底层原理文章汇总

在前面两章中详情了方法消息的解决流程,宏观上来说,方法的本质就是对消息的发送,解决消息的过程呢,我们经历了objc_msgSend快速查找、慢速查找。在前面两个环节中,仍然在本类、父类继承链、元类继承缓存中找未找到消息,又未采取动态方法决议,对未查找到的方法实现resolveInstancMethod,则就会报错奔溃。这样对于开发者来说是不愿看到的,所以对消息的解决就来到了新的层次,进行消息转发,本章内容将围绕这个开展。

铺垫

通过前面分析lookUpImpOrForward,既然是寻觅或者者转发,那在没寻觅到的情况下,它是怎样转发的呢?入口又是在哪?

//static voidlog_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer){#if SUPPORT_MESSAGE_LOGGING    if (slowpath(objcMsgLogEnabled && implementer)) {        bool cacheIt = logMessageSend(implementer->isMetaClass(),                                       cls->nameForLogging(),                                      implementer->nameForLogging(),                                       sel);        if (!cacheIt) return;    }#endif    cache_fill(cls, sel, imp, receiver);}//-------------------------------------------------bool objcMsgLogEnabled = false;static int objcMsgLogFD = -1;bool logMessageSend(bool isClassMethod,                    const char *objectsClass,                    const char *implementingClass,                    SEL selector){    char    buf[ 1024 ];    // Create/open the log file    if (objcMsgLogFD == (-1))    {        snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());        objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());        if (objcMsgLogFD < 0) {            // no log file - disable logging            objcMsgLogEnabled = false;            objcMsgLogFD = -1;            return true;        }    }    // Make the log entry    snprintf(buf, sizeof(buf), "%c %s %s %s\n",            isClassMethod ? '+' : '-',            objectsClass,            implementingClass,            sel_getName(selector));    objcMsgLogLock.lock();    write (objcMsgLogFD, buf, strlen(buf));    objcMsgLogLock.unlock();    // Tell caller to not cache the method    return false;}// 1: objcMsgLogEnabled 控制开关// 2: externvoid instrumentObjcMessageSends(BOOL flag){    bool enable = flag;    // Shortcut NOP    if (objcMsgLogEnabled == enable)        return;    // If enabling, flush all method caches so we get some traces    if (enable)        _objc_flush_caches(Nil);    // Sync our log file    if (objcMsgLogFD != -1)        fsync (objcMsgLogFD);    objcMsgLogEnabled = enable;}// SUPPORT_MESSAGE_LOGGING#endif

/tmp/这个路径就是就是将奔溃日志输出到本地临时缓存了,
在main.m中调用Book的burnBook的未实现方法,extern:这是一个关键字,是告诉编译器在编译时不要报错,在该类中不存在的方法,请去别的类查找。

objcMsgLogEnabled = true

使得objcMsgLogEnabled =true,则即可以查看到在本地生成的一个文件。调用仍然会奔溃,但log会输出的。

msgSends文件路径

文件内容-调用方法顺序
其中forwardingTargetForSelector就是快速转发方法;慢速转发则是methodSignatureForSelector,其实还搭配forwardInvocation使用。

消息转发-快速转发

我们该怎样使用forwardingTargetForSelector 呢?这个时候可以瞄一瞄苹果文档

forwardingTargetForSelector苹果说明
假如有无法识别的消息,就将其转发到指定的对象。那我们即可以进行一个操作,再定义一个English的类。假如识别到burnBook被调用,我们就将其转发给另外一个类的方法中,English类中实现了这个方法,就会在English中找寻这个方法。

// English.m-(void)burnBook{    NSLog(@"English burn book");}

Book中将方法转发出去,把目标对象返回。

#import "Book.h"#import "English.h"@implementation Book- (id)forwardingTargetForSelector:(SEL)aSelector{    if ([NSStringFromSelector(aSelector) isEqualToString:@"burnBook"]) {            return [English alloc];    }    return [super forwardingTargetForSelector:aSelector];}@end

转发到EnglishEnglish实现了该burnBook方法,最终会打印出结果,也不会报错。

转发消息到其余对象
这样我们即可以在开发过程中很好的利用这样一点,结合动态方法决议,在需要的地方增加方法,避免应用奔溃,或者者利用运行时动态执行少量自己设置方法都是很好的方向。

消息转发-慢速转发

在快速转发消息之后,就会来到慢速转发(标准转发)消息。找到运行时的方法methodSignatureForSelector

苹果解释
返回方法的签名,在苹果的文档中解释了方法的使用场景,也指出在转发消息时要创立NSInvocation对象。
我们先验证下log里面方法调用顺序,仍然是调用burnBook,但是不在forwardingTargetForSelector中解决。再实现方法签名方法,返回父类方法,程序继续奔溃,但是也表示我们的方法按照log中的顺序走下来了。
快速转发->方法签名
现在,我们对其进行签名,并实现forwardInvocation
结果-不崩溃
经过慢速转发,程序已经不再奔溃,它已经将消息转发出去,自己也不再解决。

方法签名图


image.png

我们可以查看下NSInvocation结构

NSInvocation定义
我们可以验证下签名之后的anInvocation中有哪些东西
签名后的anInvocation
表明签名后的信息都传递到了- (void)forwardInvocation:(NSInvocation *)anInvocation
我们可以在将其消息转发给English
慢速消息转发给English

消息转发流程

我们发现和我们之前分析的log文件中方法调用顺序不一致,那么我们可以再做一个操作,实现
resolveInstanceMethod,打印结果,我们就能发现,与msgSends-10393中的方法执行顺序的一致

经过一系列的转发,我们可以大致总结到以下一个流程


消息转发流程
免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境 软件环境
相关推荐
熬夜写代码,不如换女装入GitHub获上千Star?
如何更好的做单元测试(下)
异步和单线程
面试总被问分库分表怎样办?你可以这样怼他
var公告变量和作用域的一小点了解
首页
搜索
订单
购物车
我的