美文网首页
07--应用加载04--分类以及(非)懒加载分析[load_im

07--应用加载04--分类以及(非)懒加载分析[load_im

作者: 修_远 | 来源:发表于2020-07-22 01:18 被阅读0次
    TOC

    load_images 流程分析

    load_images 主要分为两个流程:

    • prepare_load_methods:准备 load 方法
    • call_load_methods:调用 load 方法

    流程一:prepare_load_methods

    处理非懒加载类

    1. 获取所有的非懒加载类(实现了load方法的类)

      classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
      
    2. 计划添加(load)类

      这个方法中有一个递归,递归父类

      保证父类的load方法在子类的load方法之前执行

      static void schedule_class_load(Class cls)
      {
          // Ensure superclass-first ordering
          schedule_class_load(cls->superclass);
      
          add_class_to_loadable_list(cls);
      }
      
    3. loadable_classes 表分析:这是一个全局数组

      • 初始化以及扩容
      if (loadable_classes_used == loadable_classes_allocated) {
          loadable_classes_allocated = loadable_classes_allocated*2 + 16;
          loadable_classes = (struct loadable_class *)
              realloc(loadable_classes,
                                loadable_classes_allocated *
                                sizeof(struct loadable_class));
      }
      
      • 添加实现load方法的类的操作
      loadable_classes[loadable_classes_used].cls = cls;
      loadable_classes[loadable_classes_used].method = method;
      loadable_classes_used++;
      
      • 全局变量的定义
      static struct loadable_class *loadable_classes = nil;
      static int loadable_classes_used = 0;
      static int loadable_classes_allocated = 0;
      
       - loadable_class:实现load方法的类的数组
       - loadable_classes_used:当前记录的类的个数
       - loadable_classes_allocated:当前创建的类的个数
      
      • loadable_class 的结构:cls + method
      struct loadable_class {
          Class cls;  // may be nil
          IMP method;
      };
      

    处理非懒加载类

    相同之处

    1. 获取所有的非懒加载分类:category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    2. 添加分类到 loadable_list 中:add_category_to_loadable_list(cat);,这个方法的实现流程和逻辑与 add_class_to_loadable_list(cls); 方法一模一样。

    不同之处

    1. 添加分类前会调用一次类的初始化方法:realizeClassWithoutSwift(cls, nil);

    2. 分类的信息是保存在 loadable_categories 变量中;

    3. 分类记录的信息是:分类(cate)+方法(method)

      struct loadable_category {
          Category cat;  // may be nil
          IMP method;
      };
      

    流程二:call_load_methods

    完整的分析了上面的流程,再来看这个流程就简直不要太简单。为什么这么说呢?

    源码很短,直接上(只保留主要代码):

    void call_load_methods(void)
    {
        void *pool = objc_autoreleasePoolPush();
    
        do {
            // 1. Repeatedly call class +loads until there aren't any more
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2. Call category +loads ONCE
            more_categories = call_category_loads();
    
            // 3. Run more +loads if there are classes OR more untried categories
        } while (loadable_classes_used > 0  ||  more_categories);
    
        objc_autoreleasePoolPop(pool);
    }
    

    苹果给出的注释已经非常详细了

    1. 调用类的load方法:call_class_loads();,就是这个 loadable_classes 数组中的类的load方法;
    2. 调用分类的load方法:call_category_loads();,就是 loadable_categories 数组中的分类的load方法;
    3. 遍历下去,直到全部调用

    这里解释了2个问题,看懂这个流程,简直太简单了,我都不想解释了。

    1. 主类的load方法和分类的load方法是否会同时调用还是只调用某一个

    会同时调用

    2. 为什么主类的load方法在分类的load方法前面调用

    因为在 call_load_methods 方法中,先调用的是 call_class_loads(); 主类的load方法,然后再调用 call_category_loads(); 分类的laod方法

    分类的初探

    分类的结构:通过cpp文件分析

    _category_t
    struct _category_t {
        const char *name;
        struct _class_t *cls;
        const struct _method_list_t *instance_methods;
        const struct _method_list_t *class_methods;
        const struct _protocol_list_t *protocols;
        const struct _prop_list_t *properties;
    };
    

    分类的结构:通过objc源码分析

    image

    在 objc 源码里面也有很清晰的判断 是否是元类

    • instance_methods:attachList 到类里面
    • class_methods: attachList 到元类里面

    为什么分类的方法会“覆盖”主类的方法

    1. 分类是在类的初始化完成之后才加载的;
    2. 分类的方法是通过attach到类的方法列表中;
    3. 根据attachLists的流程,后添加的方法被插在了列表前面;
    4. 在寻找方法的时候,会按照列表的顺序查找,如果找到了指定方法就不会再往后面寻找,所以造成了“覆盖”的假象,实际上两个方法都在方法列表中。

    懒加载类与非懒加载类

    懒加载类

    • 没有实现load方法
    • 在运行时调用这个类的时候才会加载

    非懒加载类

    • 实现了load方法
    • 如果有子类实现了load方法,则这个类也在编译阶段加载
    • 如果在其他类的load方法中被调用,则这个类也在编译阶段加载
    • 将类的加载提前到了编译阶段

    怎么理解将类的加载提前到了编译阶段?

    我们需要先知道编译阶段做了啥。

    在平时开发中,cmd+b,也就是我们常说的 编译一下,但真的只是 编译一下 吗?build 是构建的意思,既然是构建,肯定要有构建的东西出来。确实是的,构建的结果就是藏在我们xcode的项目目录的 product 文件夹下面的那个文件,俗称 APP,但实际上是一个可执行文件。所以,编译可以理解为从代码到可执行文件的过程

    这个过程经历了啥呢。前面将 [_dyld] 的时候讲过了,流程是:

    源文件(.m/.h)
    -> 预处理(.cpp)
    -> 预处理完成(.i)
    -> Compile编译(gcc)
    -> Assembly(.s)
    -> Assembly(as)
    -> 镜像文件(.o)
    -> _dyld链接
    -> 可执行文件

    类与分类的搭配加载

    1. 懒加载的分类(没有实现load)

    1. 懒加载类(没有实现load)

    1. _objc_init

    2. _read_images

    3. _getObjc2CategoryList:取得所有懒加载分类

      GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");
      GETSECT(_getObjc2NonlazyCategoryList, category_t *,    "__objc_nlcatlist");
      
    4. addUnattachedCategoryForClass:保存分类的方法到表中

      category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);
      NXMapTable *cats = unattachedCategories();
      NXMapInsert(cats, cls, list);
      
    5. load_images

    6. prepare_load_methods

    7. realizeClassWithoutSwift

    8. methodizeClass

    9. attachCategories

    2. 非懒加载类(实现了load)

    1. _objc_init
    2. _read_images
    3. _getObjc2CategoryList
    4. addUnattachedCategoryForClass
    5. remethodizeClass
    6. tachCategories

    2. 非懒加载分类(实现了load)

    1. 懒加载类(没有实现load)

    image
    1. alloc
    2. _objc_msgSend_uncached
    3. lookUpImpOrForward
    4. initializeAndLeaveLocked
    5. realizeClassWithoutSwift:在这里将所有方法存在了ro中
    6. methodizeClass

    2. 非懒加载类(实现了load)

    image
    1. _objc_init
    2. _read_images
    3. realizeClassWithoutSwift
    4. methodizeClass

    相关文章

      网友评论

          本文标题:07--应用加载04--分类以及(非)懒加载分析[load_im

          本文链接:https://www.haomeiwen.com/subject/iyenkktx.html