iOS中方法的查找分为两部分:
- 快速查找,在缓存中直接找到发送的消息
- 慢速查找,在缓存中如果没找到就到了慢速查找,先找当前类,再找父类以及父类的父类,如果找到就执行,如果找不到就执行动态方法解析和消息转发
1.代码分析方法查找流程
三者关系如图所示:
子类中对象方法的层层调用
LGStudent *student = [[LGStudent alloc] init];
// 对象方法
// 自己有 - 返回自己
[student sayHello];
// 自己没有 - 老爸有 -
[student sayNB];
// 自己没有 - 老爸没有 - NSObject
[student sayMaster];
// 自己没有 - 老爸没有 - NSObject 没有
// unrecognized selector sent to instance 0x103000450
[student performSelector:@selector(saySomething)];
打印结果:
结论:只要是继承关系,子类可以调用父类中的所有对象方法,如果一直追究到NSObject中也没有的对象方法saySomething,则会报出一个最经典最常见的错误信息:unrecognized selector sent to instance 0x100562f70
子类中类方法的层层调用
打印结果:
结论:同上,只不过这里论证的是类方法。即,几个类中不存在sayLove方法,所以会报经典错误。
子类调用NSObject中的对象方法
结论:完全遵守isa的走位流程图(https://www.jianshu.com/p/cf18f3cc1306)
2.源码分析消息查找流程
主要涉及方法:_class_lookupMethodAndLoadCache3
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
参数解读
- cls 当前类
- sel 当前方法的名字
- inst 当前类对象
- initialize 是否初始化
- cache 是否缓存
- resolver 是否在执行动态方法解析
2.如果缓存命中 直接返回imp
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
3.realizeClass 方法实现
static Class realizeClass(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro; // 方法列表 属性列表 等的存储结构题
class_rw_t *rw; // 存储 class_ro_t的结构题
Class supercls; // 父类
Class metacls; // 元类
bool isMeta; // 当前类是否是元类
ro = (const class_ro_t *)cls->data(); // 给ro赋值
isMeta = ro->flags & RO_META; // 是否是元类判断
supercls = realizeClass(remapClass(cls->superclass)); // 初始化父类
metacls = realizeClass(remapClass(cls->ISA())); // 初始化元类
cls->superclass = supercls; 找到当前类的 父类
cls->initClassIsa(metacls); 初始化当前类的元类
if (supercls) { // 如果有父类就把当前类关联到父类的子类列表中
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
4.如果类没有初始化就执行初始化 _class_initialize 的代码就不放了 主要作用就是 没有初始化的类进行初始化 如果有父类并且父类也没有初始化就连 父类也初始化掉 如果初始化完了就什么也不做 如果正在初始化 就等待
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
5.开始找方法和存储缓存
{
Method meth = getMethodNoSuper_nolock(cls, sel); // 一个for循环 从方法列表中找方法
if (meth) { // 如果找到方法 开始缓存
log_and_fill_cache(cls, meth->imp, sel, inst, cls); // 这里会调用 cache_fill 上一篇已经写过
imp = meth->imp;
goto done; // 找到直接返回
}
}
6.如果当前类没找到,就查找父类以及父类的父类,并且执行缓存,如果找到直接返回
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
7.如果以上的过程都没有找到方法,那么就执行动态方法解析,也就是 if (resolver && !triedResolver) {这个判断体中的内容}
8. _class_resolveMethod 方法实现
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
// 如果不是元类 说明是类 此时类中已经没有方法 直接执行 _class_resolveInstanceMethod
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst); // 执行 + (BOOL)resolveInstanceMethod:(SEL)sel方法
}
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); //
}
}
}
上述代码当是元类的时候: cls 就是元类 inst 类对象 这时会做两次尝试 resolveClassMethod _class_resolveInstanceMethod
9.最后动态方法解析会走到 _class_resolveInstanceMethod, 如果类没有实现resolveInstanceMethod就直接返回 如果类实现了 resolveInstanceMethod 就发送 SEL_resolveInstanceMethod消息
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*/);
}