什么时候会进行慢速查找
当快速查找也就是在缓存中找不到时,就会来到慢速查找流程,所谓的慢速查找就是当类以及类的继承链的方法列表中进行方法查找。
在快速查找时没找到需要执行的方法时,会调用__objc_msgLookup_uncached进行处理,该方法中的核心方法是MethodTableLookup,从方法列表中查询方法。
__objc_msgLookup_uncached源码
STATIC_ENTRY __objc_msgSend_uncachedUNWIND __objc_msgSend_uncached, FrameWithNoSaves// 方法列表中查找方法MethodTableLookupTailCallFunctionPointer x17END_ENTRY __objc_msgSend_uncached
MethodTableLookup源码
.macro MethodTableLookupSAVE_REGS MSGSEND// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)// receiver and selector already in x0 and x1mov x2, x16mov x3, #3// 研究的核心 方法查找的实现bl _lookUpImpOrForward// IMP in x0mov x17, x0RESTORE_REGS MSGSEND.endmacro
- MethodTableLookup方法中我们要研究的是_lookUpImpOrForward方法;
- 全局搜索_lookUpImpOrForward方法,我们发现汇编中只有调用,并找不到实现;
- 其实_lookUpImpOrForward是C来实现的,所以此处我们应该搜索lookUpImpOrForward方法。
- ※※在这里有一个小技巧,汇编方法方法名前一般有两个下划线,去掉一个下划线就是对应的C++方法的方法名,那么在去掉一个下划线就是对应C方法的方法名。
-
慢速查找
lookUpImpOrForward
首先我们全局搜索lookUpImpOrForward,可以看到在objc-runtime-new.mm中。
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){// 定义一个_objc_msgForward用于处理消息转发const IMP forward_imp = (IMP)_objc_msgForward_impcache;// 初始化一个空的IMP函数指针IMP imp = nil;// 声明当前操作的类对象Class curClass;runtimeLock.assertUnlocked();// 判断当前类是否已经初始化完成if (slowpath(!cls->isInitialized())) {behavior |= LOOKUP_NOCACHE;}runtimeLock.lock();// 判断当前类是否是一个已知类,也就是是否已经加载checkIsKnownClass(cls);// 获取当前类对象cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);// runtimeLock may have been dropped but is now locked againruntimeLock.assertLocked();// 赋值当前操作的类对象curClass = cls;// 循环遍历从继承链的方法列表中查询方法实现for (unsigned attempts = unreasonableClassCount();;) {if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {#if CONFIG_USE_PREOPT_CACHESimp = cache_getImp(curClass, sel);if (imp) goto done_unlock;curClass = curClass->cache.preoptFallbackClass();#endif} else {// curClass method list.// 采用二分法从当前类的方法列表中查询当前方法是否存在Method meth = getMethodNoSuper_nolock(curClass, sel);// 如果返回的meth存在,则说明查找到了,获取方法的imp并进行赋值,结束慢速查找流程if (meth) {imp = meth->imp(false);goto done;}// 此处判断当前类的父类是否为空,并将父类赋值给当前类,如果父类为nil时仍查找失败,则结束慢速查找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;}}// No implementation found. Try method resolver once.// 如果遍历结束,查找方法失败,则进入到消息转发流程// 会调用resolveMethod_locked进行消息转发if (slowpath(behavior & LOOKUP_RESOLVER)) {behavior ^= LOOKUP_RESOLVER;return resolveMethod_locked(inst, sel, cls, behavior);}// 完成慢速查找done:if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {#if CONFIG_USE_PREOPT_CACHESwhile (cls->cache.isConstantOptimizedCache(/* strict */true)) {cls = cls->cache.preoptFallbackClass();}#endif// 将方法加入到缓存中log_and_fill_cache(cls, imp, sel, inst, curClass);}done_unlock:runtimeLock.unlock();if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {return nil;}return imp;}
realizeAndInitializeIfNeeded_locked获取当前类对象方法
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize){runtimeLock.assertLocked();// 判断当前类是否已经实现,如果没实现需要先实现if (slowpath(!cls->isRealized())) {// 确定当前类的继承链和ISA的继承链cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);// runtimeLock may have been dropped but is now locked again}// 判断是否初始化,如果没有,则需要先进行初始化if (slowpath(initialize && !cls->isInitialized())) {cls = initializeAndLeaveLocked(cls, inst, runtimeLock);// runtimeLock may have been dropped but is now locked again// 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}return cls;}
确定当前类的继承链和ISA的继承链
确定当前类的父类,并执行递归,最终确定类的继承链,并确定跟类;
- 确定元类,并将元类的继承链也递归确定下来;
- 在realizeClassWithoutSwift中调用addSubclass来将父类与子类关联起来,并通过addRootClass来确定跟类。 ```objectivec static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) { return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); }
static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked) { lock.assertLocked();
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {// Non-Swift class. Realize it now with the lock still held.// fixme wrong in the future for objc subclasses of swift classesrealizeClassWithoutSwift(cls, nil);if (!leaveLocked) lock.unlock();} else {// Swift class. We need to drop locks and call the Swift// runtime to initialize it.lock.unlock();cls = realizeSwiftClass(cls);ASSERT(cls->isRealized()); // callback must have provoked realizationif (leaveLocked) lock.lock();}return cls;
}
static Class realizeClassWithoutSwift(Class cls, Class previously) { runtimeLock.assertLocked();
…………// 获取父类supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);// 获取元类 ISAmetacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);…………// 设置父类cls->setSuperclass(supercls);// 设置元类cls->initClassIsa(metacls);// Connect this class to its superclass's subclass listsif (supercls) {// 如果父类存在,将当前类添加为父类的子类addSubclass(supercls, cls);} else {// 否则将当前类设置为跟类addRootClass(cls);}// Attach categoriesmethodizeClass(cls, previously);return cls;
}
<a name="rbU7u"></a>### getMethodNoSuper_nolock方法列表二分查找<a name="k9AuL"></a>#### 方法查找流程getMethodNoSuper_nolock->search_method_list_inline->findMethodInSortedMethodList->findMethodInSortedMethodList<a name="JZE1o"></a>#### findMethodInSortedMethodList二分法实现二分法查找的有点:查询速度快,又称折半查找法。```objectivecfindMethodInSortedMethodList(SEL key, const method_list_t *list, const getNameFunc &getName){ASSERT(list);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) {// `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)getName((probe - 1))) {probe--;}return &*probe;}if (keyValue > probeValue) {base = probe + 1;count--;}}return nil;}
在父类中进行查找
- 我们通过源码可以发现for循环其实是一个死循环,只能通过循环中的break来调出循环。
也就是只有当imp == forward_imp或者curClass->getSuperclass()==nil时才会调出循环。
for (unsigned attempts = unreasonableClassCount();;) {…………if (slowpath((curClass = curClass->getSuperclass()) == nil)) {// No implementation found, and method resolver didn't help.// Use forwarding.imp = forward_imp;break;}…………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;}…………}
当在类中的methodList中没有找到方法时
- 首先执行slowpath((curClass = curClass->getSuperclass())将curClass设置为父类
- 判断父类是否为nil,如果为nil则调出循环
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {// No implementation found, and method resolver didn't help.// Use forwarding.imp = forward_imp;break;}
在父类的缓存中查找方法
- 当前curClass指向的是父类,所以此时查找会查找父类的缓存;
- 如果在父类的缓存中没有找到,就会进入下次循环,从父类的方法列表中进行慢速查找;
- 在执行慢速查找时,如果仍然未找到,则会继续将curClass置为父类的父类,继续查找;
- 直到当父类为nil时,此时会将imp赋值为forward_imp,跳出循环,结束查找流程;
- 或者在父类中找到方法,将查找到的方法赋值给imp,跳出循环,结束查找流程;
动态方法决议
当慢速查找结束仍为找到方法实现时,则会进入到动态方法决议流程if (slowpath(behavior & LOOKUP_RESOLVER)) {behavior ^= LOOKUP_RESOLVER;return resolveMethod_locked(inst, sel, cls, behavior);}
找到方法,加入缓存
找到方法后,会将方法写入缓存,方便下次调用可以快速查找log_and_fill_cache(cls, imp, sel, inst, curClass);
