引言
上篇中在探索方法的本质是通过objc_msgSend(sel, id)
进行消息发送,我们知道objc_msgSend(sel, id)
是通过汇编
实现的,在cache
中找不到方式时走到了lookUpImpOrForward
,然而这个方法是通过c++
实现的,为什么objc_msgSend(sel, id)
是用汇编实现的?这样做的好处在于,一是汇编
语言更接近机器语言,并且使用了缓存快速优化查找时间,二是当我们参数不确定时(动态参数)c++
不能更好的适配,而汇编
更加的动态化。下面分析一下lookUpImpOrForward
源码(捡主要代码分析)。
lookUpImpOrForward 源码分析
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
// 1.
checkIsKnownClass(cls);
// 2.
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
curClass = cls;
// 3.
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
method_t *meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp(**false**);
goto done;
}
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
}
// Halt if there is a cycle in the superclass chain.
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
done_unlock:
return nil
return imp;
}
1.checkIsKnownClass(cls)
checkIsKnownClass(cls)
是判断当前类是否已注册在缓存表中。主要代码:
isKnownClass(Class cls)
{
if (fastpath(objc::dataSegmentsRanges.contains(cls->data()->witness, (uintptr_t)cls))) {
return true;
}
auto &set = objc::allocatedClasses.get();
return set.find(cls) != set.end() || dataSegmentsContain(cls);
}
2.realizeClassWithoutSwift(Class, Class)
realizeClassWithoutSwift(Class, Class)
实现类。主要有下面两部分
2.1 从磁盘中获取类的数据,并赋值给当前类
// 从磁盘中获取到类的数据,data中包含了methodList, property, protocol 等数据
auto ro = (const class_ro_t *)cls->data();
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
// 将从磁盘中的数据赋值给当前类
cls->setData(rw);
2.2 按照isa继承链
初始化类
按照isa继承链 递归初始化类
,主要代码实现如下:
// 获取类的父类 -> ... -> NSObject -> nil 按照这个链条加载类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
// 获取元类的父类 -> ... -> 根元类 -> NSOject -> nil 按照这个链条加载元类
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// 给类赋值父类
cls->setSuperclass(supercls);
// 给元类赋值父元类
cls->initClassIsa(metacls);
3.二分查找法遍历methodList
3.1 getMethodNoSuper_nolock(curClass, sel)
从cls->data()->methods()
获取方法列表,这里的methods
是二维数组,search_method_list_inline(*mlists, sel)
对二维数据进行排序
,并使用二分法进行查找imp
。
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel){
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists) {
method_t *m =
if (m) return m;
}
return nil;
}
3.2 findMethodInSortedMethodList(sel, method_list_t,getNameFunc)
对methodsList
排序后,使用二分法查找imp
static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list, const getNameFunc &getName){
auto first = list->begin();
auto base = first;
decltype(first) 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)getName(probe);
if (keyValue == probeValue) {
// This is required for correct category overrides.
// 当类的实例方法和分类的方法相同时,分类的方法会覆盖类的方法
while (probe > first && keyValue == (uintptr_t)getName((probe - 1))) {
probe--;
}
return &*probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
例如,0-9,我们要查找的数是7,使用上方的算法走一遍流程:
keyValue = "7", base = 0, list = 0-9
第一轮:
count = 10,
probe = base + (count >> 1) = 0 + 1010 >> 1 = 0101 = 5,
probeValue = "5",
"7" > "5", base = probe + 1 = 5 + 1 = 6, count = 9
第二轮:
count >>= 1, 即 1001 >> 1 = 0100 = 4
probe = base + (count >> 1) = 6 + 0100 >> 1 = 6 + 0010 = 8
peobeValue = "8"
"8" > "7" 不成立
第三轮:
count >> 1 = 0100 > 1= 0010 = 2
probe = base + (count >> 1) = 6 + 0010 >> 1= 6 + 0001 = 6 + 1 = 7
probeValue = "7"
keyValue == probeValue = "7"
便找到了我们要的数据。
4. 父类查找方法
如果使用二分查找
在当前类
没有找到相应的method
。
① 将当前类的父类赋值给当前类,如果当前类的父类是nil,则imp = forward_imp,进行方法转发
② 首先使用cache_getImp()使用汇编的方式快速查找方法,如果汇编中没有找到该方法,则依旧会来到消息的慢速查找流程中
小结:方法的查找流程为,当前类 -> 父类 -> ... -> NSObject -> nil
,若中间找到方法,则写入缓存,若当查找到nil
的时候也就是找遍了继承链也没找到方法时,则将imp = forward_imp
进行方法转发。
// 将当前类的父类赋值给当前类
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
// 如果当前类的父类是nil,则imp = forward_imp,进行方法转发
imp = forward_imp;
break;
}
// 首先使用cache_getImp()使用汇编的方式快速查找方法,如果汇编中没有找到该方法,则依旧会来到消息的慢速查找流程中
imp = cache_getImp(curClass, sel);
5. done || done_unlock
done
写入缓存中:
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
done_unlock
消息转发:
imp == forward_imp