Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)

栏目: Objective-C · 发布时间: 5年前

内容简介:当消息发没有从子类和父类查找到实现的时候,Runtime会给我们补救的机会。我们称为消息转发机制。这个机制分为源码里lookForwardImp里写着 如果没有实现该方法但是实现了resolve为true,且triedResolver为false则进入**_class_resolveMethod**,只会解析一次如果解析完成triedResolver就会变为true,进不来如果这个类不是元类则执行__

当消息发没有从子类和父类查找到实现的时候,Runtime会给我们补救的机会。我们称为消息转发机制。这个机制分为 动态方法解析转发

Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)

动态方法解析

// No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);//消息转发
        runtimeLock.read();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);
复制代码

源码里lookForwardImp里写着 如果没有实现该方法但是实现了resolve为true,且triedResolver为false则进入**_class_resolveMethod**,只会解析一次如果解析完成triedResolver就会变为true,进不来

/***********************************************************************
* _class_resolveMethod
* Call +resolveClassMethod or +resolveInstanceMethod.
* Returns nothing; any result would be potentially out-of-date already.
* Does not check if the method already exists.
**********************************************************************/
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        _class_resolveClassMethod(cls, sel, inst);
        
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}
复制代码

如果这个类不是元类则执行__ class_resolveInstanceMethod ,如果是元类的话则执行__ class_resolveClassMethod ,所以如果是类方法的话执行 resolveClassMethod ,那么会一直查找最后还会执行一次resolveinstancemethod, 但是这个方法是执行的元类的resolveinstancemethod而不是类的 ,因为类是元类对象如果元类找不到就会往上层查找,元类的上层是根元类,根元类的父类指向NSObject,所以最后还会执行一次resolveInstanceMethod最终的实例方法。**,如果是实例方法则resolveInstanceMethod方法进行解析,那么我们在实际运行的时候会发现执行了两次解析方法,因为__ class_resolveInstanceMethod 又帮我们发送了一次消息

/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (__typeof__(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); /// 执行两次的原因 

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

复制代码

然后可以在这个方法里面进行resolveInstanceMethod返回true的时候动态增加方法。进行处理,如果不在这里进行处理则消息进入转发流程。通过下面的isa走位图也可以清晰的查找出来。

Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)

消息转发流程

// No implementation found, and method resolver didn't help. 
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);
复制代码

上面的动态方法解析如果没有拦截则会进入__ objc_msgForward_impcache 这个方法,这个方法是由汇编进行调用的,没有源码实现。源码实现是闭源的。大家都知道没有拦截会进行快速转发 forwardTarget ,然后快速转发会通过 methodSginatureForSelector 进行方法签名慢速转发.,我们要分析的肯定不能这么肤浅需要更深层次的来进行解析

extern void instrumentObjcMessageSends(Bool)

void 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;
}
复制代码

这个函数可以进行打印信息比如打印一些底层log,这些信息最终会储存到一些地方地址是**./private/tmp** 目录,可以通过这些函数查看底层调用了那些信息那么怎么使用这个函数呢。 定义一个Person类 定义一个walk方法,注意之前一定要写上 instrumentObjcMessageSends(YES)instrumentObjcMessageSends(NO)方法

Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)
Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)
Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)

这里就看到了消息转发的执行过程

initialize -> resolveClassMethod -> resolveInstanceMethod -> forwardingTargetForSelector -> methodSignatureForSelector -> doesNotRecognizeSelector

./private/tmp那么这个路径是怎么来的我们可以看到instrumentObjcMessageSends的源码里有一个objcMsgLogEnabled确认可以打印信息 然后执行objcMsgLogFD == -1 这是一个静态属性 然后logMessageSend会执行 当objcMsgLogFD = -1的时候会输出log当目录。

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;
}
复制代码

那么我们看到log输入的流程当我们抛出异常的时候系统是如何来成功抛出来的呢,看到objc_msgForward_impcache的方法解析后执行了__objc_forward_handler

Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)

然后_objc_forward_handler在文件里面是优雅的将崩溃信息抛出来了

// Default forward handler halts the process.
__attribute__((noreturn)) void 
objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
复制代码

以上所述就是小编给大家介绍的《Runtime底层原理探究(三) --- 消息转发机制(动态方法解析)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Automate This

Automate This

Christopher Steiner / Portfolio / 2013-8-9 / USD 25.95

"The rousing story of the last gasp of human agency and how today's best and brightest minds are endeavoring to put an end to it." It used to be that to diagnose an illness, interpret legal docume......一起来看看 《Automate This》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具