本文由快学吧个人写作,以任何方式转载请表明原文出处

一、资料预备

objc4-818.2

对应mac的版本是11.1。可依据自己的体系版本挑选能够进行调试的源码

二、思路

  1. 类的加载(上)和类的加载(下)中知道了非懒加载类和懒加载类是怎样从镜像加载到内存中并完结的。
  2. methodizeClass源码中有关分类的处理还没有看过。那么分类的实质是什么?
  3. 分类是怎样和主类联系上的,分类又是怎样完结的?

三、分类的实质

设置main.m中的代码,创立一个JDMan的分类 :

二十二、app加载流程(六)分类的实现和加载(上)

经过clang编译main.m文件,生成编译文件检查分类的实质。一定要进入到main.m所在文件夹下,再执行clang指令。

clang -rewrite-objc main.m -o main.cpp

1. 实质是结构体category_t

在main.cpp中能够看到分类的实质是结构体category_t

二十二、app加载流程(六)分类的实现和加载(上)

在818源码中能够查找一下:struct category_t

二十二、app加载流程(六)分类的实现和加载(上)

name : 分类的姓名

cls : 哪个类的分类

instanceMethods : 分类的实例办法列表,类型为method_list_t

classMethods : 分类的类办法列表,类型为method_list_t

protocols : 分类的协议列表,类型为protocol_list_t

instanceProperties : 分类中的特色,类型为property_list_t

官方注释 :下面的不常见,不说了。

2. 分类的特色

  1. 在分类中经过经过@property增加的特色是没有setter和getter办法的,和类不相同。
  2. 分类的特色增加setter和getter办法能够经过相关目标来设置。后边再说。

在上面编译的main.cpp文件中也能够看出来,分类中的办法列表中不自带setter和getter办法 :

二十二、app加载流程(六)分类的实现和加载(上)

四、探究分类加载的预备

1. 分类加载的总体思路

分类的加载就要分红4种状况了,因为分类是依附于类的。

二十二、app加载流程(六)分类的实现和加载(上)

删除去main.m中创立的JDPerson分类,以文件的方式来创立一下。用来测验分类的加载。创立成果如下图 :

二十二、app加载流程(六)分类的实现和加载(上)

main.m如下图 :

二十二、app加载流程(六)分类的实现和加载(上)

2. 分类的加载是从哪里进行的

  1. 思路 :
  1. 类的完结都是经过realizeClassWithoutSwift,不管是非懒加载类仍是懒加载类。
  2. realizeClassWithoutSwift中的methodizeClass中发现了官方注释Attach categories.
  3. 在官方注释下面的methodizeClass源码中的attachToClass源码中发现了attachCategories,经过命名猜想是和分类相关。attachCategories源码中有对rwe的操作。而rwe是运转时动态修正类才会呈现的结构。
  1. 大局查找attachCategories,找到load_categories_nolock呈现了对它调用。

  2. 大局查找load_categories_nolock,找到了loadAllCategories

二十二、app加载流程(六)分类的实现和加载(上)

  1. 大局查找loadAllCategories,找到了load_images

二十二、app加载流程(六)分类的实现和加载(上)

  1. load_images是在之前的objc_init中注册回调dyld,使其加载镜像的函数。

  2. 在map_images一章中见过load_images。是经过dyld的注册回调,用来加载镜像。

  3. 从查找的流程看,是契合map_images这章中的read_images中对分类的处理的官方注释的。

  4. 那么完结类的realizeClassWithoutSwift中的methodizeClass,它的attachToClass是没有对类的分类进行操作吗?

3. 创立分类进行测验

1. JDMan和main.m中的代码

main.m :

这儿删除去了上面探究分类实质的时分在main.m中创立的分类代码。

二十二、app加载流程(六)分类的实现和加载(上)

JDMan :

二十二、app加载流程(六)分类的实现和加载(上)

2. 创立分类

二十二、app加载流程(六)分类的实现和加载(上)

3. 测验用的自己写的代码段 :

代码段1 :

            const char *qudaodemingzi = cls->mangledName();
            const char *JDManName = "JDMan";
            if (strcmp(qudaodemingzi, JDManName) == 0) {
                printf("找到自己界说的类了 : %s 从函数 : %s 拿到的\n",qudaodemingzi,__func__);
            }

代码段2 :

    const char *qudaodemingzi = cls->mangledName();
    const char *JDManName = "JDMan";
    if (strcmp(qudaodemingzi, JDManName) == 0) {
        auto jd_ro = (const class_ro_t *)cls->data();
        auto jd_isMeta = jd_ro->flags & RO_META;
        if (!jd_isMeta) {
            printf("找到自己界说的类了:%s 从函数 : %s 拿到的\n",qudaodemingzi,__func__);
        }
    }

代码段3 :

    const char *qudaodemingzi = cls->mangledName();
    const char *JDManName = "JDMan";
    if (strcmp(qudaodemingzi, JDManName) == 0) {
        auto jd_isMeta = cls->isMetaClass();
        if (!jd_isMeta) {
            printf("找到自己界说的类了 :%s 从函数 : %s 拿到的\n",qudaodemingzi,__func__);
        }
    }
4. 将代码段贴到源码中

用到的代码段不相同,也是依据类的完结状况来确定的。能够自行调整。

  1. read_images中,找到非懒加载类的完结,用代码段1 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. realizeClassWithoutSwift中,用代码段2 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. methodizeClass中,用代码段3 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. attachToClass中,在if判别平分别加了代码段3 :

(1). attachToClass的if外部 :

二十二、app加载流程(六)分类的实现和加载(上)

(2). attachToClass的if内部 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 进入load_categories_nolock,参加代码段3 ,稍作修正,将printf里边直接改成load_categories_nolock,因为这是个迭代器,所以__func__只会显现operator(),改成如下图 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 进入attachCategories,参加代码段3 :

二十二、app加载流程(六)分类的实现和加载(上)

五、只要一个分类,分类的加载状况

就如上面创立的,JDMan只要一个分类JDMan(LA)。

1. 类非懒加载+分类非懒加载

read_imagesrealizeClassWithoutSwiftload_categories_nolockattachCategories中自界说的代码段中参加断点,:

二十二、app加载流程(六)分类的实现和加载(上)

二十二、app加载流程(六)分类的实现和加载(上)

二十二、app加载流程(六)分类的实现和加载(上)

运转项目,成果如下 :

  1. 先进入了read_images的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 再进入了realizeClassWithoutSwift的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 然后进入了load_categories_nolock的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 最后进入了attachCategories的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 再运转,就会完结程序的运转,得到了流程如下 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 能够发现,是没有进入attachToClassif判别的。

小结 :

非懒加载类 + 非懒加载分类的加载 :

(1). 非懒加载类经过: read_images—>realizeClassWithoutSwift—>methodizeClass—>attachToClass先进行加载和完结。

(2). 非懒加载分类经过 :

load_images—>loadAllCategories—>load_categories_nolock—>attachCategories再进行加载和完结。并没有进入attachToClassif判别的流程。

2. 类非懒加载+分类懒加载

首要注释掉JDMan(LA).m这个分类中的+(void)load办法。分类变成懒加载。

运转项目,成果如下 :

  1. 先进入了read_images的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 然后进入了realizeClassWithoutSwift的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 项目直接运转完毕,看自界说代码段打印的成果 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 并没有进入load_images的流程。那么分类是否是和类一同加载的?

  2. 从头运转项目,将断点走到realizeClassWithoutSwift里边。然后向下执行断点,到auto isMeta = ro->flags & RO_META;这一行 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 使用lldb检查ro的数据 :

二十二、app加载流程(六)分类的实现和加载(上)

二十二、app加载流程(六)分类的实现和加载(上)

小结 :

非懒加载类 + 懒加载分类 :

懒加载分类的数据会在编译的时分就和非懒加载类一同被存入了非懒加载类的ro中。在非懒加载类加载的时分,一同加载到内存。不需求经过attachCategories等过程再进行加载。

3. 类懒加载+分类非懒加载

注释掉JDMan类中的+(void)load。翻开JDMan(LA)分类中的+(void)load

运转项目,成果如下 :

  1. 先进入了read_images的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

2.然后进入了realizeClassWithoutSwift的断点 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 项目直接运转完毕,看自界说代码段打印的成果 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 并没有进入load_images的流程。那么分类非懒加载是否会使类也成为非懒加载?在read_images非懒加载类的循环那里,参加如下测验代码,如图 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 再次运转818.2项目。

二十二、app加载流程(六)分类的实现和加载(上)

  1. JDMan呈现在了非懒加载类中,证明非懒加载的分类使类也变成了非懒加载的。那么分类的数据是否也现已存储到了类中?从头运转818.2,然后使断点执行到realizeClassWithoutSwift中,移动断点到ro下面,检查ro中的数据。

二十二、app加载流程(六)分类的实现和加载(上)

小结 :

懒加载类 + 非懒加载分类 :

非懒加载的分类会使懒加载的类变成非懒加载,分类数据和类的数据在编译期一同被写入ro。在类加载的时分,一同加载到内存。不需求经过attachCategories等过程再进行加载。

4. 类懒加载+分类懒加载

翻开类和分类的+(void)load办法。在main.m中让类被调用一次,例如在main.m参加JDMan *aMan = [[JDMan alloc] init];

运转项目,成果如下 :

  1. 进入了realizeClassWithoutSwift的断点,只不过是经过lookUpImpOrForward进入的 :

二十二、app加载流程(六)分类的实现和加载(上)

  1. 然后就直接运转完结,不会走其他的断点。

  2. 那么分类是否也和上面相同,直接和类一同完结了加载?从头运转项目,看ro的数据 :

二十二、app加载流程(六)分类的实现和加载(上)

小结 :

懒加载类+懒加载分类 : 类会在第一次被调用的时分完结完结和加载,分类的完结和加载与类一同完结。分类的数据存储在ro中。

六、总结

  1. 分类实质是结构体category_t
  2. 分类中增加的特色不会自动生成setter和getter办法,需求用到相关目标。
  3. 假如只要一个分类的状况下,分类的完结和加载状况如下 :

(1). 非懒加载类 + 非懒加载分类 :

非懒加载类的加载 : map_images—>read_images—>realizeClassWithoutSwift—>methodizeClass—>attachToClass

非懒加载分类的加载 : load_images—>loadAllCategories—>load_categories_nolock—>attachCategories

只要一个分类,非懒加载分类不会进入类的methodizeClass—>attachToClass—>attachCategories流程中进行加载。

(2). 非懒加载类 + 懒加载分类 :

分类的数据会在编译期就被写入类的ro中。伴随着类的加载完结加载。不进入attachCategories

(3). 懒加载类 + 非懒加载分类 :

非懒加载分类会使懒加载的类变成非懒加载的类,分类的数据也会在编译期就被写入到类的ro中。分类会伴随着类的加载完结加载。不进入attachCategories

(4). 懒加载类 + 懒加载分类 :

分类的数据也会在编译期就被写入类的ro中。当类第一次被调用,经过lookUpImpOrForward进行完结和加载的时分,分类伴随着一同完结。不进入attachCategories