前言
我们上篇文章知道方法调用首先通过快速查找,如果没有找到的话,就会走慢速查找流程,那慢速查找流程是怎样呢
慢速查找流程
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
// 初始化参数
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 缓存中查找
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
// 当类没有初始化时,初始化类和父类、元类等,保证后面方法的查找流程
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
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
// 防止动态添加方法,缓存会变化,再次查找缓存。imp = cache_getImp(cls, sel);
// 如果找到imp方法地址, 直接调用done, 返回方法地址
imp = cache_getImp(cls, sel);
if (imp) goto done;
// 查找方法列表, 传入类对象和方法名
// Try this class's method lists.
{
// 根据sel去类对象里面查找方法
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 如果方法存在,则缓存方法,
// 内部调用的就是 cache_fill。
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
// 方法缓存之后, 取出函数地址imp并返回
imp = meth->imp;
goto done;
}
}
// 如果类方法列表中没有找到, 则去父类的缓存中或方法列表中查找方法
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
// 如果父类缓存列表及方法列表均找不到方法,则去父类的父类去查找。一层层进行递归查找,直到找到NSObject类
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.
// 查找父类的cache_t缓存
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
// 在父类中找到方法, 在本类中缓存方法, 注意这里传入的是cls, 将方法缓存在本类缓存列表中, 而非父类中
log_and_fill_cache(cls, imp, sel, inst, curClass);
// 执行done, 返回imp
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
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;
}
}
}
// ---------------- 消息发送阶段完成,没有找到方法实现,进入动态解析阶段 ---------------------
// No implementation found. Try method resolver once.
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;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
// ---------------- 动态解析阶段完成,进入消息转发阶段 ---------------------
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
下面的动态解析和消息转发我们先不看,主要看上面,通过流程大致可以看出,方法找的顺序为
- 首先缓存中查找,没有往下走
- 通过getMethodNoSuper_nolock在类找,找到就缓存,返回没有往下
- 通过父类中找,继续1,2步骤
- 都没有找到走动态解析,没有往下
- 进入消息转发阶段
下面我们具体看下getMethodNoSuper_nolock的实现
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
这个是个简单的遍历查找过程,继续看方法search_method_list的实现
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
可以看出,如果方法列表已经排序了,就进行排序查找,如果没有就走遍历查找过程。 看下查找算法
static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
assert(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
看出,是通过二分法查找,进行查找的
总结
- 慢速查找通过c/c++语言实现的
- 先通过getMethodNoSuper_nolock找本类的,如果没有找父类
- 如果没有找到,就走动态解析阶段
- 如果还没有,就走消息转发阶段
- 如果没有就报错