前言:在上文objc_msgSend流程分析中方法查询到
lookUpImpOrForward
,其中对方法进行循环向上查找看其父类中是否存在,如果在父类对方法缓存列表中找到就返回Imp指针调用相应方法,如果没有怎么办?
咱在接下来对方法中一块探个究竟!
一.慢速查找流程
- 如果找到方法,返回
imp
并goto done
,并保存方法到调用此方法到类到方法
缓存列表,代码如下:
if (meth) {
imp = meth->imp;
goto done;
}
...省略部分代码
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
- 遍历上层的父类还是没有查找到方法给
imp
赋值imp = forward_imp
,并跳出循环,下一步执行方法如下:
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
可见lookUpImpOrForward多次调用,if判断里只执行一次判断这就让resolveMethod_locked方法显得尤为重要(逻辑运算符计算)

二.动态方法解析(也叫决议)
1. resolveMethod_locked
具体干了什么?
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 (!lookUpImpOrNil(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
这里主要有两个分支,主要是对cls
做个是否元类的判断::
- 不是元类,意味着调用的是实例方法,那么执行
resolveInstanceMethod
函数 - 是元类,说明调用的是类方法,执行
resolveClassMethod
函数,之后如果依然没找到IMP
,则再去执行resolveInstanceMethod
函数;
1.1 resolveInstanceMethod
实例方法解析
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
// Resolver not implemented.
// 如果你没有实现类方法 +(BOOL)resolveInstanceMethod:(SEL)sel
// NSObject也有实现,所以一般不会走这里
// 注意这里传入的第一个参数是:cls->ISA(),也就是元类
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;// 调用类方法: +(BOOL)resolveInstanceMethod:(SEL)sel
bool resolved = msg(cls, resolve_sel, sel);// 再找一次imp(这次是sel,而不是resolveInstanceMethod)
// 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(inst, sel, cls);
// 检测是否有sel对应的IMP。假如你在+(BOOL)resolveInstanceMethod:(SEL)sel中添加了sel的函数地址IMP,此时再次去查找这个IMP就能找到。
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));
}
}
}
- 检查类方法是否有方法实现:
SEL resolve_sel = @selector(resolveInstanceMethod:)
; -
lookUpImpOrNil
这里的lookUpImpOrForward
中的resolver为NO
,所以只会在本类和父 类中查找,并不会动态方法解析。 - 向本类发送
SEL_resolveInstanceMethod
消息,resolved = msg(cls, resolve_sel, sel)
- 再次查找当前实例方法
imp
,找到就填充缓存
,找不到就返回IMP imp = lookUpImpOrNil(inst, sel, cls)
; - 结束动态方法解析,回到
lookUpImpOrForward
方法将triedResolver
置否并goto retry
重新查找缓存
和方法列表
resolveInstanceMethod
函数先后调用了两次lookUpImpOrNil:
-
容错判断if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) ,是为了看当前类以及父类、元类等有没有实现resolveInstanceMethod:,如果都没有就没必要继续往下走了。(NSObject里面是默认有这个方法的)
-
如果实现了,就通过objc_msgSend,向当前cls发送消息,也就是调用resolveInstanceMethod:这个已经实现的方法,在这个方法里,我们已经手动给sel添加了一个imp
-
然后再通过lookUpImpOrNil检查一遍,拿到我们添加的imp
-
最后返回到lookUpImpOrForward方法,重新循环去找,最终返回imp
-
注意到这两次调用中,
resolver
都是NO
,因此在其调用lookUpImpOrForward
时不会触发 消息的解析,仅仅是从“类、父类、...、根类
”的缓存中和方法列表
中找IMP
,没找到会触发 消息转发。
1.2 resolveClassMethod
类方法解析
跟实例方法相比,类方法稍微复杂了一点
// 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_resolveClassMethod
进入resolveClassMethod
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(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 resolveClassMethod:%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));
}
}
}
在经过resolveClassMethod的处理之后,如果依然没有找到类方法的IMP,就会再次执行resolveInstanceMethod函数!不同于实例方法的是,此时的cls是元类,因此msg(cls, SEL_resolveInstanceMethod, sel);即是向元类内部发送resolveInstanceMethod:消息,也就意味着是根类调用resolveInstanceMethod:方法(这次只能在根类的分类中补救了),同时缓存查找类方法的IMP仅发生在根元类和根类中,而方法列表中查找类方法的IMP则分别在“元类、元类的父类、...、根元类、根类”中进行。
当我们调用一个类方法时,如果在类中没有实现,同时在resolveClassMethod中也没有处理,那么最终会调用根类(NSObject)的同名实例方法。
三.消息转发
1.3.举个例子
类PHFather中一个实例方法和类方法,且在.m中没有实现


运行结果如下图:

什么鬼?但是从堆栈信息中可以看到有forwarding这个调用函数
方法的调用经过了查找、解析,如果还是没有找到IMP,就会来到消息转发流程。它的入口在lookUpImpOrForward函数靠后的位置
https://juejin.im/post/6844904063524405255
网友评论