OC 底层探索 10、objc_msgSend 流程 2 - 慢速查找

我们已经知道消息发送流程首先会走到缓存 cache 里面,那么当缓存中没有查询到消息时 __objc_msgSend_uncached,后续怎么继续执行呢?

一、切入口

__objc_msgSend_uncached --> MethodTableLookup --> _lookUpImpOrForward.

我们通过源码可以看到缓存中找不到会进入 _lookUpImpOrForward 的查找过程,汇编中搜索并未找到此方法:

1、全局搜索‘lookUpImpOrForward’,可以发现此方法是在objc-runtime-new.mm文件中C++实现的;

2、同样也可以通过 show 反汇编方式找到:Debug --> Debug Workflow --> Always show disassembly 勾上

control + stepinto 进入 objc_msgSend

control + stepinto 进入 _objc_msgSend_uncached

_lookUpImpOrForward -->  objc-runtime-new.mm6116 行。

下面将从 lookUpImpOrForward 为入口进行消息查找流程的探索。

二、慢速查找分析

1、慢速查找流程图:

 

forward_imp:--> const IMP forward_imp = (IMP)_objc_msgForward_impcache;

这里代码会继续找到汇编里面:--> __objc_msgForward --> _objc_forward_handler :

经典的报错信息!“+ -” 的打印时苹果人为手动添加的,这里其实也可说明在底层并不存在所谓的 +- 方法,都是函数而已,实例方法是类的实例方法,类方法也是元类的实例方法。

2、主要源码

1. 查找的主流程

  1 IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
  2 {// cache 中没有找到方法,开始走 lookUpImpOrForward 查找流程
  3     // 汇编有调 --> objc_msgForward --> 找不到方法报错信息的处理
  4     const IMP forward_imp = (IMP)_objc_msgForward_impcache;
  5     IMP imp = nil;
  6     Class curClass;
  7 
  8     runtimeLock.assertUnlocked();
  9 
 10     // Optimistic cache lookup
 11     if (fastpath(behavior & LOOKUP_CACHE)) {
 12         // 找缓存 - 是为了出现在此过程中方法又被人调用加进缓存了,有缓存了就不必继续慢速找了
 13         imp = cache_getImp(cls, sel);
 14         if (imp) goto done_nolock;// 找到了,去 done_nolock
 15     }
 16 
 17     // runtimeLock is held during isRealized and isInitialized checking
 18     // to prevent races against concurrent realization.
 19 
 20     // runtimeLock is held during method search to make
 21     // method-lookup + cache-fill atomic with respect to method addition.
 22     // Otherwise, a category could be added but ignored indefinitely because
 23     // the cache was re-filled with the old value after the cache flush on
 24     // behalf of the category.
 25     // 注释翻译不如英文准确不翻了
 26     runtimeLock.lock();
 27 
 28     // We don't want people to be able to craft a binary blob that looks like
 29     // a class but really isn't one and do a CFI attack.
 30     //
 31     // To make these harder we want to make sure this is a class that was
 32     // either built into the binary or legitimately registered through
 33     // objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair.
 34     //
 35     // TODO: this check is quite costly during process startup.
 36     checkIsKnownClass(cls);
 37 
 38     if (slowpath(!cls->isRealized())) {// cls 是否已实现,否则去将类信息进行处理 类元类方法全部要处理好的 --> 为了后面的方法查找
 39         cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
 40         // runtimeLock may have been dropped but is now locked again
 41     }
 42 
 43     // initialize 初始化
 44     if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
 45         cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
 46         // runtimeLock may have been dropped but is now locked again
 47 
 48         // If sel == initialize, class_initialize will send +initialize and 
 49         // then the messenger will send +initialize again after this 
 50         // procedure finishes. Of course, if this is not being called 
 51         // from the messenger then it won't happen. 2778172
 52     }
 53 
 54     runtimeLock.assertLocked();
 55     curClass = cls;
 56 
 57     // The code used to lookpu the class's cache again right after
 58     // we take the lock but for the vast majority of the cases
 59     // evidence shows this is a miss most of the time, hence a time loss.
 60     //
 61     // The only codepath calling into this without having performed some
 62     // kind of cache lookup is class_getInstanceMethod().
 63 
 64     for (unsigned attempts = unreasonableClassCount();;) {// 死循环,没有出口条件,跳出逻辑在循环内部
 65         // curClass method list.
 66         Method meth = getMethodNoSuper_nolock(curClass, sel);// 查找方法
 67         if (meth) {// 找着了
 68             imp = meth->imp;
 69             goto done;
 70         }
 71         // 自己没找着
 72         // curClass = superClass
 73         // 是nil 则直接 没找着方法把nil的forward_imp赋给imp,并跳出循环
 74         if (slowpath((curClass = curClass->superclass) == nil)) {
 75             // No implementation found, and method resolver didn't help.
 76             // Use forwarding.
 77             imp = forward_imp;
 78             break;
 79         }
 80 // superclass 不是 nil 继续向下走
 81 
 82         // Halt if there is a cycle in the superclass chain.
 83         // 如果超类链中存在循环,则停止
 84         if (slowpath(--attempts == 0)) {
 85             _objc_fatal("Memory corruption in class list.");// 类列表的内存污染了
 86         }
 87 
 88         // Superclass cache.
 89         // 找父类的缓存
 90         /**
 91          CacheLookup  GETIMP, _cache_getImp
 92          */
 93         imp = cache_getImp(curClass, sel);
 94         /*
 95          STATIC_ENTRY _cache_getImp
 96 
 97          GetClassFromIsa_p16 p0
 98          CacheLookup GETIMP, _cache_getImp // GETIMP,cache查找的参数是GETIMP,checkMiss
 99          
100     LGetImpMiss:// cache 中没找到直接返回0
101          mov    p0, #0
102          ret
103 
104          END_ENTRY _cache_getImp
105          */
106         
107         
108         if (slowpath(imp == forward_imp)) {//
109             // Found a forward:: entry in a superclass.
110             // Stop searching, but don't cache yet; call method
111             // resolver for this class first.
112             break;
113         }
114         if (fastpath(imp)) {// 父类中找到了 goto done --> 对此方法进行缓存
115             // Found the method in a superclass. Cache it in this class.
116             goto done;
117         }
118     }
119 
120     // No implementation found. Try method resolver once.
121   // 上面找完了没找着方法 动态方法解析 resolver 一次 --> 此方法只会走一次once
122     if (slowpath(behavior & LOOKUP_RESOLVER)) {/*
                                &:3 & 2 = 0011 & 0010 = 0010
                                第二次(方法动态处理中会再回来查一遍)再来条件就为false了: 0001 & 0010 = 0000
                                */
123 behavior ^= LOOKUP_RESOLVER;// 异或操作 behavior = 0011^0010 = 0001 124 return resolveMethod_locked(inst, sel, cls, behavior); 125 } 126 127 done: 128 log_and_fill_cache(cls, imp, sel, inst, curClass); 129 runtimeLock.unlock(); 130 done_nolock: 131 if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) { 132 return nil; 133 } 134 return imp; 135 }

2. 二分查找方法 list 源码与注解

 1 /***********************************************************************
 2  * search_method_list_inline
 3  **********************************************************************/
 4 ALWAYS_INLINE static method_t *
 5 findMethodInSortedMethodList(SEL key, const method_list_t *list)
 6 {
 7     ASSERT(list);
 8     // list: 方法list是递增排序(类加载时完成的排序)的 即:name 转 unsigned long 类型 对应的数值 是递增的,例:0 1 2 3 4 5 ......
 9     const method_t * const first = &list->first;
10     const method_t *base = first;// 方法list中第一个方法
11     const method_t *probe;
12     uintptr_t keyValue = (uintptr_t)key;// 要查找的方法  sel强转uintptr_t
13     uint32_t count;// 方法数
14     // 二分查找
15     /**
16      找 03   -    01 02 03 04 05 06 07 08
17      probe = 1 +  8>>1 = 1+ 4 = 5
18      判断第5个方法是否是要找的方法
19         不是:对比 要找的方法 和 当前方法 位置谁大 03>05?false
20      
21      开始回第一步
22      probe = 1+ count>>1 = 1 +  4>>1 = 1+2 = 3
23      继续判断...
24      02 == 03 false
25      02 > 03 false
26      
27      继续
28      probe = 1+ 2>>1 = 1+1 = 2
29      02 == 02 ture
30         while 判断
31             判断找有重名的分类方法
32      */
33     for (count = list->count; count != 0; count >>= 1) {// count = 8>>1  4>>1  2>>1
34         probe = base + (count >> 1);
35         
36         uintptr_t probeValue = (uintptr_t)probe->name;
37         
38         if (keyValue == probeValue) {// 判断这个方法是否是要找的方法
39             // `probe` is a match.
40             // Rewind looking for the *first* occurrence of this value.
41             // This is required for correct category overrides.
42             while (probe > first && keyValue == (uintptr_t)probe[-1].name) {// 判断分类方法 是否有重名的方法,有则往前找取分类方法 --> 分类排在前面
43                 probe--;
44             }
45             return (method_t *)probe;
46         }
47         
48         if (keyValue > probeValue) {
49             base = probe + 1;
50             count--;
51         }
52     }
53     
54     return nil;
55 }

 

posted @ 2020-09-25 22:38  张张_z  阅读(294)  评论(0编辑  收藏  举报