美文网首页
13、分类的加载分析

13、分类的加载分析

作者: ChenL | 来源:发表于2020-10-20 16:07 被阅读0次

    在main中定义LGperson的分类LG

    @interface LGPerson (LG)
    
    /*
     *cate_name
     */
    @property (nonatomic,copy) NSString *cate_name;
    
    /*
     *cate_age
     */
    @property (nonatomic,assign) int cate_age;
    
    - (void)cate_instanceMethod1;
    - (void)cate_instanceMethod2;
    - (void)cate_instanceMethod3;
    - (void)cate_sayClassMethod;
    @end
    
    
    @implementation LGPerson (LG)
    
    - (void)cate_instanceMethod1{
        NSLog(@"%s",__func__);
    }
    - (void)cate_instanceMethod2{
        NSLog(@"%s",__func__);
    }
    - (void)cate_instanceMethod3{
        NSLog(@"%s",__func__);
    }
    - (void)cate_sayClassMethod{
        NSLog(@"%s",__func__);
    }
    
    @end
    

    一、分类的本质

    探索分类的本质,有以下三种方式:
    1、通过 clang
    2、通过Xcode 文档搜索Category
    3、通过objc源码搜索 category_t

    方式一、通过 clang

    打开终端:clang -rewrite-objc main.m -o main.cpp 查看底层编译,即 main.cpp
    其分类的类型是_category_t

    分类的倒数第二个0,表示的是没有协议,所以赋值为0

    image.png

    搜索struct_category_t

    image.png
    其中有两个method_list_t,分别表示 实例方法类方法

    搜索 CATEGORY_INSTANCE_METHODS_LGPerson,找到其底层实现

    image.png
    image.png

    格式为:sel+签名+地址,是method_t 结构体的属性即key

    image.png

    搜索method_t
    name 对应 sel
    type 对应 方法签名
    imp 对应 函数地址

    image.png

    注意:分类中的属性是没有set、get方法

    image.png

    查看_prop_list_t,在分类中定义了属性,但是在底层编译中并没有看到属性,如上图,这是因为分类中定义的属性没有相应的set、get方法。下一篇,我们通关关联对象来设置.

    方式二、通过Xcode文档搜索 Category
    image.png
    方式三:通过objc源码搜索 category_t
    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
        
        protocol_list_t *protocolsForMeta(bool isMeta) {
            if (isMeta) return nullptr;
            else return protocols;
        }
    };
    

    二、分类的加载

    先创建LGPerson的两个分类 LGA、LGB

    在类的加载分析中可以得到,realizeClassWithoutSwift -> methodizeClass -> attachToClass -> load_categories_nolock -> extAlloc ->attachCategories中提及了rwe的加载,其中分析了分类的data数据是如何加载到类中的,且分类的加载顺序是:LGA LGB 的顺序加载到类中 (越晚加进来,越在前面

    image.png

    查看methodizeClass的源码实现,可以发现 类的数据分类的数据是分开的处理的。主要是因为在编译阶段,就已经确定好了 方法的归属位置(即:实例方法存储在类中,类方法存储在元类中),而分类是后面才加进来的。

    其中分类需要通过 attatchToClass添加到类,然后才能在外界进行使用,在此过程中,我们已经知道了分类加载三步骤的后面的两个步骤了

    分类加载的三步骤:

    1、分类数据加载时机: 根据 类和分类是否实现load方法 来区分不同的时机
    2、attachCategories 准备分类数据
    3、attachLists 将分类数据添加到主类中

    三、分类的加载时机分析

    第一种情况:

    LGPerson 类、LGPerson+LGA、LGPerson+LGB 均实现+load方法为例

    在走到了attachCategories方法时,必然会有分类数据的加载,可以通过反推法 查看在什么时候调用 attachCategoriegs的,通过查找,有两个方法中调用了,

    image.png

    addToClass 方法中,这里经过调试,从来不会进到if流程中,除非加载两次,一般的一般只会加载一次

    image.png

    全局搜索load_categories_nolock的调用,有两次调用:
    1、一次是loadAllCategories方法中

    image.png

    2、一次在_read_images

    image.png

    但是经过调试发现,是不会走_read_images方法中的if流程的,而是走loadAllCategories方法中

    image.png

    全局搜索查看 loadAllCategories的调用,发现是load_images时调用的


    image.png

    总结一下:
    该情况下的分类数据加载时机 的反推 路径为:attachCategories -> load_categories_nolock -> loadAllCategories -> load_images

    而我们的分类加载正常的流程的路径为:realizeClassWithoutSwift -> methodizeClass -> attachToClass ->attachCategories

    分类数据加载时机.png
    第二种情况:

    LGPerson 类、LGPerson+LGA、实现+load方法,LGPerson+LGB 不实现+load方法为例

    image.png

    继续往下执行,会再次来到 attachCategories方法中断住

    image.png

    总结一下:

    只要有一个分类是非懒加载分类,那么所有的分类都会被标记为非懒加载分类,换言之,加载一次 已经开辟了rwe,就不会再次懒加载,重新去处理LGPerson

    由上面的两个例子,我们可以大致将类 和 分类 是否实现+load的情况分为4类:

    分类和类的搭配使用

    image.png

    【情况1】非懒加载类 + 非懒加载分类

    【情况2】非懒加载类 + 懒加载分类

    【情况3】懒加载类 + 懒加载分类

    【情况4】懒加载类 + 非懒加载分类

    对于各个情况就不一一分析了,我们直接来看结果吧!😄😄

    image.png

    【情况1】非懒加载类 + 非懒加载分类 :
    其数据的加载在load_images方法中,首先对类进行加载,然后把分类的信息添加到类中

    【情况2】非懒加载类 + 懒加载分类
    其数据加载在read_images就加载数据,数据来自data,data在编译阶段就已经完成,即data中除了类的数据,还有分类的数据,与类绑定在一起

    【情况3】懒加载类 + 懒加载分类
    其数据加载推迟到 第一次消息时,数据同样来自data,data在编译时期就已经完成。

    【情况4】懒加载类 + 非懒加载分类
    只要分类实现了load,会迫使主类提前加载,即在_read_images中不会对类做实现操作,需要在load_images方法中触发类的数据加载,即rwe初始化,同时加载分类数据

    相关文章

      网友评论

          本文标题:13、分类的加载分析

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