上一篇关于方法的本质
的探索中,我们知道了方法
的底层是调用objc_msgSend
发送消息,并对objc_msgSend
的底层汇编进行了分析。当用汇编
快速查找,未查找到方法缓存
时,会调用 MethodTableLookup
,然后调用_class_lookupMethodAndLoadCache3
,从汇编
转到C
,开启一系列的慢速查找,接下来我们对_class_lookupMethodAndLoadCache3
的方法查找流程进行分析。
_class_lookupMethodAndLoadCache3
方法查找流程假如当调用LGStudent
调用对象方法sayHello
时,底层通过objc_msgSend
发送消息,通过汇编在LGStudent
的 cache中快速查找sayHello
的缓存,未找到时,会来的_class_lookupMethodAndLoadCache3
,方法如下:
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
// NO/*cache*/ 没有方法缓存
}
在IMP lookUpImpOrForward()
方法中
cache
,为ture
时,再次通过cache_getImp(cls, sel)
方法,用汇编去查找imp
,查找到直接返回imp
;为false
时,直接跳过。!cls->isRealized()
,调用realizeClass(cls)
,做准备工作(根据class
中rw data()->flags & RW_REALIZED
),比如父类 元类 rw ro
等。 if (!cls->isRealized()) {
realizeClass(cls);
}
cache_getImp(cls, sel)
,查找到imp
,直接返回imp
. imp = cache_getImp(cls, sel);
if (imp) goto done;
试图在 class's method lists
中查找方法
,通过getMethodNoSuper_nolock
获取meth
4.1. getMethodNoSuper_nolock
中:
循环取出mlists
后,调用method_t *m = search_method_list(*mlists, sel)
,通过sel
去匹配,匹配到直接返回。
4.2. 方法
找到后,调用log_and_fill_cache
,然后调用cache_fill (cls, sel, imp, receiver)
,调用cache_fill_nolock(cls, sel, imp, receiver)
,进入方法
缓存流程,判断是否有缓存,是否超出容量的3/4,是否需要扩容,然后找到bucket
, 偏移_occupied
,然后set(key, imp)
,将方法缓存到Class
的cache_t cache
中,方便下次调用时,快速查找。
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
当class's method lists
中未找到方法时,即:sayHello
方法在LGStudent
中未定义,那么会试图在父类的缓存和方法列表中superclass caches and method lists
查找。
在查找superclass
过程中,按照父类
-> 元类
-> 根元类
-> 根类(NSObject)
的顺序,依次循环查找,先通过汇编cache_getImp
查找superclass
的cache
,缓存命中,调用log_and_fill_cache
,进入方法
缓存流程直接返回imp
; 在缓存中未找到时,查找Superclass method list
,流程同 4.1、4.2 步骤,源码如下:
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// 进入方法缓存流程
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
// 进入方法缓存流程
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
class's method lists
和superclass caches and method lists
中都没有找到调用的方法时,即:调用的方法
在类 父类 元类
中都未实现,那么调用_class_resolveMethod(cls, sel, inst)
方法,进行方法动态解析。 // 源码
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// 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;
}
_class_resolveMethod
方法实现void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) { // 判断是否是元类
// try [cls resolveInstanceMethod:sel]
// 类 此时类中已经没有方法 直接执行 _class_resolveInstanceMethod
// 执行 + (BOOL)resolveInstanceMethod:(SEL)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
方法实现:
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
// 查找_resolveInstanceMethod方法,
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*/);
}
在_class_resolveInstanceMethod
方法中,
先通过上面的方法查找流程查找resolveInstanceMethod
方法的IMP
(该方法是系统NSObject
默认实现,+ (BOOL)resolveInstanceMethod:(SEL)sel
,默认返回 NO
),查找到IMP
后,向cls
发送resolveInstanceMethod
消息,参数是sel
(为实现的方法)。
所以,我们可以在重新系统的 resolveInstanceMethod 方法,在此方法中对为实现的方法进行动态解析,
防止因为调用未实现的方法引起的系统崩溃。
假如,我们在resolveInstanceMethod
方法中,对方法进行了动态解析,那么这个方法的IMP
,会加入到对应的cache
中,然后跳转到 步骤6,然后retry
,重新查找。
retry
之后,依然没有查找到IMP
,调用下面汇编
。__objc_msgForward_impcache
的汇编代码:__objc_forward_handler
,从汇编
调用OC
方法,如下:补充:重新resolveInstanceMethod
示例:
+ (BOOL)resolveInstanceMethod:(SEL)sel{
// saySomething为为实现方法,当调用次方法时,就调用已经实现的sayHello方法
if (sel == @selector(saySomething)) {
IMP sayHIMP = class_getMethodImplementation(self, @selector(sayHello));
Method sayHMethod = class_getInstanceMethod(self, @selector(sayHello));
const char *sayHType = method_getTypeEncoding(sayHMethod);
return class_addMethod(self, sel, sayHIMP, sayHType);
}
NSLog(@"来了 老弟 - %p",sel);
return [super resolveInstanceMethod:sel];
}
定义下面代码: LGStudent
调用 对象方法 sayMaster
,是否会崩溃?为什么?
@interface LGPerson : NSObject
- (void)sayNB;
+ (void)sayHappay;
@end
#import "LGPerson.h"
@implementation LGPerson
- (void)sayNB{
NSLog(@"%s",__func__);
}
+ (void)sayHappay{
NSLog(@"%s",__func__);
}
@end
//
#import "LGPerson.h"
@interface LGStudent : LGPerson
- (void)sayHello;
+ (void)sayObjc;
@end
#import "LGStudent.h"
@implementation LGStudent
- (void)sayHello{
NSLog(@"%s",__func__);
}
+ (void)sayObjc{
NSLog(@"%s",__func__);
}
@end
@interface NSObject (LG)
- (void)sayMaster;
+ (void)sayEasy;
@end
@implementation NSObject (LG)
- (void)sayMaster{
NSLog(@"%s",__func__);
}
+ (void)sayEasy{
NSLog(@"%s",__func__);
}
[LGStudent sayMaster];
@end
答:不会崩溃,LGStudent
继承自 LGPerson
,LGStudent
调用 sayMaster
方法,因为本身没有 sayMaster
方法,会去父类LGPerson
中寻找,LGPerson
同样没有 sayMaster
方法,接下来寻找 LGPerson
的元类,直到寻到 根元类,而 根元类 也没有 sayMaster
方法,最后寻找 根元
类 的 父类NSObject
, 父类NSObject
中有对象方法 sayMaster
,所以不会崩溃,并且会调用该方法。