前言
在之前的探索中,我们已经基本了解了在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,不会再跳进来了。
点击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。