消息处理流程之动态方法决议

前言

在之前的探索中,我们已经基本了解了在oc中关于方法的快速查找以及慢速查找的流程,现在一个新的问题是,由于runtime的运行时机制,我们声明的函数方法,在不实现的情况下,并不会报错,编译是可以通过的,但是在运行时会崩溃,那么到底在这个时候方法做了什么操作呢?接下来就是本次需要探索的问题。

关于_objc_msgForward_impcache

在进入到lookUpImpOrForward方法中以后,很明显第一行代码,就给imp赋予了一个默认的指针,类型是_objc_msgForward_impcache,那么我们来看下这个_objc_msgForward_impcache的类型。点击系统源码并没有相应的实现,那么就进入到汇编中查看。
全局搜索__objc_msgForward

        STATIC_ENTRY __objc_msgForward_impcache
	b	__objc_msgForward
	END_ENTRY __objc_msgForward_impcache
复制代码

可以看到仅仅是跳转了__objc_msgForward,那么再次搜索

        ENTRY __objc_msgForward

	adrp	x17, __objc_forward_handler@PAGE
	ldr	p17, [x17, __objc_forward_handler@PAGEOFF]
	TailCallFunctionPointer x17
	
	END_ENTRY __objc_msgForward
复制代码

再次全局搜索__objc_forward_handler,发现搜不到,这时候可以根据之前跳到lookupimp的经验,这边又要跳回到源码中。

全局搜索objc_forward_handler

void *_objc_forward_handler = nil;
void *_objc_forward_stret_handler = nil;

#else

// Default forward handler halts the process.
__attribute__((noreturn, cold)) 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;

复制代码

这里也就可以看得出来,如果找不到方法,就会报错unrecognized selector sent to instanceXXX,其中包括了+、—方法,也就是不存在类或者对象方法,在底层都是对象方法。

如果说这个_objc_msgForward_impcache类型最终经过一系列处理最终找不到还是会报找不到方法的错误,那么中间到底做了哪些处理呢?

resolveMethod_locked

在本类、父类或者元类都没有找到方法,我们在最终处理之前打下一个断点

拓展

这里是一个单例的实现,behavior是3,LOOKUP_RESOLVER是2,两个&是2,然后3^2是0,当下次再进来时,0^任何数都是0,不会再跳进来了。

截屏2021-07-01 下午5.40.28.png

点击resolveMethod_locked进来,这里系统没有因为你给的imp是nil而直接崩溃,出于人道主义精神,系统依然给你一次改过自新的机会,

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();

    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
复制代码

系统最终又调用了lookUpImpOrForwardTryCache,点击看一下这个方法

IMP lookUpImpOrForwardTryCache(id inst, SEL sel, Class cls, int behavior)
{
    return _lookUpImpTryCache(inst, sel, cls, behavior);
}
复制代码

再点击_lookUpImpTryCache方法,系统又开始判断是否初始化,然后缓存查找,共享换缓存有没有?慢速查找?,又重新开始了。。。但是这个重新走不是白走,因为之前就有了if else的处理,那么接下来,就很清晰了,我们就要看看这个if else里面做了什么处理?仔细一看,定睛一瞧,resolveInstanceMethod这个方法都调用了,很明显,它就是关键了。

ALWAYS_INLINE
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertUnlocked();

    if (slowpath(!cls->isInitialized())) {
        // see comment in lookUpImpOrForward
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

    IMP imp = cache_getImp(cls, sel);
    if (imp != NULL) goto done;
#if CONFIG_USE_PREOPT_CACHES
    if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
        imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
    }
#endif
    if (slowpath(imp == NULL)) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

done:
    if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
        return nil;
    }
    return imp;
}
复制代码

resolveInstanceMethod

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, 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 = lookUpImpOrNilTryCache(inst, sel, cls);

    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这个方法,那么还是可以拯救一下的~接下来就是针对你实现的这个方法resolve_sel做查询。
如果类没有实现resolveInstanceMethod,那么当前的sel就是nil,此时走第一个if不会被return,因为系统默认会给了实现,返回no。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享