• 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 }
  • 相关阅读:
    Excel操作 Microsoft.Office.Interop.Excel.dll的使用
    C#通过Microsoft.Office.Interop.Word操作Word
    Swift编码总结2
    Swift编码总结1
    Python第一阶段06
    Python第一阶段05
    Python第一阶段04
    Python第一阶段03
    Python第一阶段02
    Python第一阶段01
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13720971.html
Copyright © 2020-2023  润新知