美文网首页
OC底层原理14 - 类的加载之分类

OC底层原理14 - 类的加载之分类

作者: 卡布奇诺_95d2 | 来源:发表于2021-03-04 15:12 被阅读0次

    类的懒加载和非懒加载

    OC底层原理13 - 类的加载过程的分析中知道realizeClassWithoutSwift函数的主要作用有:

    • 首次初始化类
    • 分配读写数据rw
    • 返回类的真实结构
    • 将类标记为已加载rw->flags = RW_REALIZED|RW_REALIZING|isMeta;

    因此,类是否完成加载即可通过查看是否调用realizeClassWithoutSwift函数.

    类的懒加载

    类的懒加载:在类中未实现了+load方法,将MachO中类的数据加载至内存的过程推迟至第一次方法调用时,在进行消息查找的过程中调用realizeClassWithoutSwift函数完成.

    当类未实现+load方法时,其调用堆栈如下:

    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
    * frame #0: 0x00000001002f6965 libobjc.A.dylib`realizeClassWithoutSwift(cls=HQPerson, previously=0x0000000000000000) at objc-runtime-new.mm:2659:57
    frame #1: 0x0000000100313530 libobjc.A.dylib`realizeClassMaybeSwiftMaybeRelock(cls=HQPerson, lock=0x000000010035a080, leaveLocked=false) at objc-runtime-new.mm:2928:9
    frame #2: 0x000000010030354f libobjc.A.dylib`realizeClassMaybeSwiftAndUnlock(cls=HQPerson, lock=0x000000010035a080) at objc-runtime-new.mm:2945:12
    frame #3: 0x00000001002f6200 libobjc.A.dylib`initializeAndMaybeRelock(cls=0x0000000100008298, inst=0x00000001000082c0, lock=0x000000010035a080, leaveLocked=true) at objc-runtime-new.mm:2244:19
    frame #4: 0x00000001003170ea libobjc.A.dylib`initializeAndLeaveLocked(cls=0x0000000100008298, obj=0x00000001000082c0, lock=0x000000010035a080) at objc-runtime-new.mm:2269:12
    frame #5: 0x00000001002ff7a5 libobjc.A.dylib`realizeAndInitializeIfNeeded_locked(inst=0x00000001000082c0, cls=0x0000000100008298, initialize=true) at objc-runtime-new.mm:6390:15
    frame #6: 0x00000001002ff23a libobjc.A.dylib`lookUpImpOrForward(inst=0x00000001000082c0, sel="alloc", cls=0x0000000100008298, behavior=11) at objc-runtime-new.mm:6500:11
    frame #7: 0x00000001002d7c1b libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #8: 0x000000010033c057 libobjc.A.dylib`objc_alloc_init [inlined] callAlloc(cls=HQPerson, checkNil=true, allocWithZone=false) at NSObject.mm:1937:12
    frame #9: 0x000000010033bfae libobjc.A.dylib`objc_alloc_init(cls=HQPerson) at NSObject.mm:1967
    frame #10: 0x00000001000039c9 HQObjc`main(argc=1, argv=0x00007ffeefbff4d0) at main.m:157:28 [opt]
    frame #11: 0x00007fff6f1eccc9 libdyld.dylib`start + 1
    

    类的非懒加载

    类的非懒加载:在类中实现了+load方法,将MachO中类的数据加载至内存的过程是在map_images中调用realizeClassWithoutSwift函数完成.

    当类实现+load方法时,其调用堆栈如下:

    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
    * frame #0: 0x00000001002f6965 libobjc.A.dylib`realizeClassWithoutSwift(cls=HQPerson, previously=0x0000000000000000) at objc-runtime-new.mm:2659:57
    frame #1: 0x00000001002d41d6 libobjc.A.dylib`_read_images(hList=0x00007ffeefbf36d0, hCount=261, totalClasses=22057, unoptimizedTotalClasses=22057) at objc-runtime-new.mm:3835:13
    frame #2: 0x00000001002d3008 libobjc.A.dylib`map_images_nolock(mhCount=261, mhPaths=0x00007ffeefbf4e00, mhdrs=0x00007ffeefbf4060) at objc-os.mm:596:9
    frame #3: 0x00000001002d2b63 libobjc.A.dylib`map_images(count=261, paths=0x00007ffeefbf4e00, mhdrs=0x00007ffeefbf4060) at objc-runtime-new.mm:3165:12
    frame #4: 0x0000000100019668 dyld`dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), bool, bool) + 1749
    frame #5: 0x0000000100019809 dyld`dyld::registerObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*)) + 63
    frame #6: 0x00007fff6f1d3ad1 libdyld.dylib`_dyld_objc_notify_register + 113
    frame #7: 0x00000001002d261a libobjc.A.dylib`_objc_init at objc-os.mm:939:5
    frame #8: 0x000000010044f0bc libdispatch.dylib`_os_object_init + 13
    frame #9: 0x000000010045fafc libdispatch.dylib`libdispatch_init + 282
    frame #10: 0x00007fff6c144791 libSystem.B.dylib`libSystem_initializer + 220
    frame #11: 0x000000010002f1d3 dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 535
    frame #12: 0x000000010002f5de dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
    frame #13: 0x0000000100029ffb dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 493
    frame #14: 0x0000000100029f66 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 344
    frame #15: 0x00000001000280b4 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #16: 0x0000000100028154 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #17: 0x0000000100016662 dyld`dyld::initializeMainExecutable() + 129
    frame #18: 0x000000010001bbba dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 6667
    frame #19: 0x0000000100015227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #20: 0x0000000100015025 dyld`_dyld_start + 37
    

    分类

    分类(Category)是OC中的特有语法,它是表示一个指向分类的结构体category_t的指针.

    struct category_t {
        //分类名称
        const char *name;
        //分类所属的类
        classref_t cls;
        //分类中定义的实例方法列表
        WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
        //分类中定义的类方法列表
        WrappedPtr<method_list_t, PtrauthStrip> 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;
    };
    

    接下来通过clang将HQPerson+HQA.m编译成C++代码来看看category_t结构.
    clang语句:clang -rewrite-objc HQPerson+HQA.m -o HQPerso+HQA.cpp

    static struct _category_t _OBJC_$_CATEGORY_HQPerson_$_HQA __attribute__ ((used, section ("__DATA,__objc_const"))) = 
    {
        "HQPerson",    //分类名称
        0, // &OBJC_CLASS_$_HQPerson,  //分类所属的类
        (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_HQPerson_$_HQA,  //分类中定义的实例方法列表
        0, //分类中定义的类方法列表
        0, //分类中定义的协议列表
        (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_HQPerson_$_HQA,    //分类中定义的属性列表
    };
    static void OBJC_CATEGORY_SETUP_$_HQPerson_$_HQA(void ) {
        _OBJC_$_CATEGORY_HQPerson_$_HQA.cls = &OBJC_CLASS_$_HQPerson;
    }
    
    • 1: 这里分类的名称显示的是"HQPerson",但是在运行时,runtime会将其修改成分类的名称.
    • 2: 在结构体中所属的父类为0,但在运行时,会调用OBJC_CATEGORY_SETUP_$_HQPerson_$_HQA静态函数,将其所属的类修改成&OBJC_CLASS_$_HQPerson.
    • 3: 分类中未定义类方法,因此类方法处为0.
    • 4: 分类中未定义协议,因此协议也为0.
    • 5: _OBJC_$_CATEGORY_INSTANCE_METHODS_HQPerson_$_HQA指定一个实例方法列表
    struct _objc_method {
        struct objc_selector * _cmd;
        const char *method_type;
        void  *_imp;
    };
    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[3];
    }
    _OBJC_$_CATEGORY_INSTANCE_METHODS_HQPerson_$_HQA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        3,
        {{(struct objc_selector *)"instanceHQAMethod2", "v16@0:8", (void *)_I_HQPerson_HQA_instanceHQAMethod2},
        {(struct objc_selector *)"instanceMethod1", "v16@0:8", (void *)_I_HQPerson_HQA_instanceMethod1},
        {(struct objc_selector *)"instanceHQAMethod1", "v16@0:8", (void *)_I_HQPerson_HQA_instanceHQAMethod1}}
    };
    
    * [参数1]:_objc_method结构体的大小
    * [参数2]:方法个数,分类中共有3个方法
    * [参数3]:方法列表
    
    • 6: _OBJC_$_PROP_LIST_HQPerson_$_HQA指定一个属性列表
    struct _prop_t {
        const char *name;
        const char *attributes;
    };
    static struct /*_prop_list_t*/ {
        unsigned int entsize;  // sizeof(struct _prop_t)
        unsigned int count_of_properties;
        struct _prop_t prop_list[1];
    }
    _OBJC_$_PROP_LIST_HQPerson_$_HQA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_prop_t),
        1,
        {{"catName","T@\"NSString\",&,N"}}
    };
    
    * [参数1]: _prop_t大小
    * [参数2]: 属性个数,分类中定义了一个名为catName的属性
    * [参数3]: 属性列表
    

    总结:

    • 分类是对原有类进行扩展.可以访问原有类的.h文件中的属性.
    • 从源码中看到,在分类中可以定义实例方法/对象方法/属性/协议.
    • 当在分类中添加属性时,并不会为属性添加getter/setter方法以及相应的私有成员变量.如本示例中,定义了catName的属性,但在方法列表中并未看见相应的getter/setter方法,而且在category_t结构中也未查看存储成员变量的地方.注意:若需要在分类中使用分类,可以使用关联对象的方法.
    • 当原有类和分类中有相同的方法名时,当进行方法查找时会优先找到分类中方法的IMP,这是因为一个类永远只有一个类对象,分类中的方法会合并至类的方法列表中,后面会详细描述合并过程.

    分类的加载

    分类最重要的一个用途是,为原有类添加方法,在OC中一个类永远只有一个类对象,那分类中的方法是如何合并到原有类中的呢?
    在原有类中存在两种加载方式,即懒加载与非懒加载,那分类是否也有实现+load方法和未实现+load方法的区别呢?
    接下来开始进行分类方法加载的探索.

    主类实现+load方法 + 分类未实现+load方法

    根据类加载的原理,当主类实现+load方法时,类的初始化流程:map_images -> realizeClassWithoutSwift.类的实例方法会在methodizeClass中进行处理.因此,直接将断点停在methodizeClass中,查看当前方法列表中的数据.

    method_list_t *list = ro->baseMethods();
    
    (lldb) p list
    (method_list_t *) $0 = 0x0000000100008040
    (lldb) p $0->get(0).big()
    (method_t::big) $1 = {
      name = "instanceHQBMethod2"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003bd0 (HQObjc`-[HQPerson(HQB) instanceHQBMethod2] at HQPerson+HQB.m:16)
    }
    (lldb) p $0->get(1).big()
    (method_t::big) $2 = {
      name = "instanceMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003c00 (HQObjc`-[HQPerson(HQB) instanceMethod1] at HQPerson+HQB.m:20)
    }
    (lldb) p $0->get(2).big()
    (method_t::big) $3 = {
      name = "instanceHQBMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003c30 (HQObjc`-[HQPerson(HQB) instanceHQBMethod1] at HQPerson+HQB.m:24)
    }
    (lldb) p $0->get(3).big()
    (method_t::big) $4 = {
      name = "instanceHQAMethod2"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x00000001000039b0 (HQObjc`-[HQPerson(HQA) instanceHQAMethod2] at HQPerson+HQA.m:17)
    }
    (lldb) p $0->get(4).big()
    (method_t::big) $5 = {
      name = "instanceMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x00000001000039e0 (HQObjc`-[HQPerson(HQA) instanceMethod1] at HQPerson+HQA.m:21)
    }
    (lldb) p $0->get(5).big()
    (method_t::big) $6 = {
      name = "instanceHQAMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003a10 (HQObjc`-[HQPerson(HQA) instanceHQAMethod1] at HQPerson+HQA.m:25)
    }
    (lldb) p $0->get(6).big()
    (method_t::big) $7 = {
      name = "instancePersonMethod2"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003aa0 (HQObjc`-[HQPerson instancePersonMethod2] at HQPerson.m:17)
    }
    (lldb) p $0->get(7).big()
    (method_t::big) $8 = {
      name = "instanceMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003ad0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $0->get(8).big()
    (method_t::big) $9 = {
      name = "instancePersonMethod1"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003b00 (HQObjc`-[HQPerson instancePersonMethod1] at HQPerson.m:25)
    }
    (lldb) p $0->get(9).big()
    (method_t::big) $10 = {
      name = "name"
      types = 0x0000000100003f8c "@16@0:8"
      imp = 0x0000000100003b30 (HQObjc`-[HQPerson name] at HQPerson.h:18)
    }
    (lldb) p $0->get(10).big()
    (method_t::big) $11 = {
      name = "setName:"
      types = 0x0000000100003f94 "v24@0:8@16"
      imp = 0x0000000100003b60 (HQObjc`-[HQPerson setName:] at HQPerson.h:18)
    }
    (lldb) p $0->get(11).big()
    (method_t::big) $12 = {
      name = ".cxx_destruct"
      types = 0x0000000100003f78 "v16@0:8"
      imp = 0x0000000100003b90 (HQObjc`-[HQPerson .cxx_destruct] at HQPerson.m:11)
    }
    

    从lldb的结果中可以看出,类的实例方法/分类HQA/分类HQB的方法,在编译时期已经合并.

    主类未实现+load方法 + 分类未实现+load方法

    当主类未实现+load方法时,类的初始化操作推迟至第一次方法调用时,流程:lookUpImpOrForward -> realizeClassWithoutSwift,类的实例方法会在methodizeClass中进行处理.因此,直接将断点停在methodizeClass中,查看当前方法列表中的数据.

    method_list_t *list = ro->baseMethods();
    (lldb) p list
    (method_list_t *) $3 = 0x0000000100008040
    (lldb) p $3->get(0).big()
    (method_t::big) $4 = {
      name = "instanceHQBMethod2"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003be0 (HQObjc`-[HQPerson(HQB) instanceHQBMethod2] at HQPerson+HQB.m:16)
    }
    (lldb) p $3->get(1).big()
    (method_t::big) $5 = {
      name = "instanceMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003c10 (HQObjc`-[HQPerson(HQB) instanceMethod1] at HQPerson+HQB.m:20)
    }
    (lldb) p $3->get(2).big()
    (method_t::big) $6 = {
      name = "instanceHQBMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003c40 (HQObjc`-[HQPerson(HQB) instanceHQBMethod1] at HQPerson+HQB.m:24)
    }
    (lldb) p $3->get(3).big()
    (method_t::big) $7 = {
      name = "instanceHQAMethod2"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x00000001000039f0 (HQObjc`-[HQPerson(HQA) instanceHQAMethod2] at HQPerson+HQA.m:17)
    }
    (lldb) p $3->get(4).big()
    (method_t::big) $8 = {
      name = "instanceMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003a20 (HQObjc`-[HQPerson(HQA) instanceMethod1] at HQPerson+HQA.m:21)
    }
    (lldb) p $3->get(5).big()
    (method_t::big) $9 = {
      name = "instanceHQAMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003a50 (HQObjc`-[HQPerson(HQA) instanceHQAMethod1] at HQPerson+HQA.m:25)
    }
    (lldb) p $3->get(6).big()
    (method_t::big) $10 = {
      name = "instancePersonMethod2"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003ab0 (HQObjc`-[HQPerson instancePersonMethod2] at HQPerson.m:17)
    }
    (lldb) p $3->get(7).big()
    (method_t::big) $11 = {
      name = "instanceMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003ae0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $3->get(8).big()
    (method_t::big) $12 = {
      name = "instancePersonMethod1"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003b10 (HQObjc`-[HQPerson instancePersonMethod1] at HQPerson.m:25)
    }
    (lldb) p $3->get(9).big()
    (method_t::big) $13 = {
      name = "name"
      types = 0x0000000100003f86 "@16@0:8"
      imp = 0x0000000100003b40 (HQObjc`-[HQPerson name] at HQPerson.h:18)
    }
    (lldb) p $3->get(10).big()
    (method_t::big) $14 = {
      name = "setName:"
      types = 0x0000000100003f8e "v24@0:8@16"
      imp = 0x0000000100003b70 (HQObjc`-[HQPerson setName:] at HQPerson.h:18)
    }
    (lldb) p $3->get(11).big()
    (method_t::big) $15 = {
      name = ".cxx_destruct"
      types = 0x0000000100003f72 "v16@0:8"
      imp = 0x0000000100003ba0 (HQObjc`-[HQPerson .cxx_destruct] at HQPerson.m:11)
    }
    

    从lldb的结果中可以看出,类的实例方法/分类HQA/分类HQB的方法,也是在编译时期已经合并.

    主类实现+load方法 + 分类实现+load方法

    当主类和分类同时都实现+load方法时,主类的类初始化流程为:map_images -> realizeClassWithoutSwift.类的实例方法会在methodizeClass进行处理,接下来仍然在methodizeClass方法中查看当前方法列表中的值.

    method_list_t *list = ro->baseMethods();
    
    (lldb) p list
    (method_list_t *) $0 = 0x00000001000081d0
    (lldb) p $0->get(0).big()
    (method_t::big) $2 = {
      name = "instancePersonMethod2"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003a80 (HQObjc`-[HQPerson instancePersonMethod2] at HQPerson.m:17)
    }
    (lldb) p $0->get(1).big()
    (method_t::big) $3 = {
      name = "instanceMethod1"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003ab0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $0->get(2).big()
    (method_t::big) $4 = {
      name = "instancePersonMethod1"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003ae0 (HQObjc`-[HQPerson instancePersonMethod1] at HQPerson.m:25)
    }
    (lldb) p $0->get(3).big()
    (method_t::big) $5 = {
      name = "name"
      types = 0x0000000100003f82 "@16@0:8"
      imp = 0x0000000100003b10 (HQObjc`-[HQPerson name] at HQPerson.h:18)
    }
    (lldb) p $0->get(4).big()
    (method_t::big) $6 = {
      name = "setName:"
      types = 0x0000000100003f8a "v24@0:8@16"
      imp = 0x0000000100003b40 (HQObjc`-[HQPerson setName:] at HQPerson.h:18)
    }
    (lldb) p $0->get(5).big()
    (method_t::big) $7 = {
      name = ".cxx_destruct"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003b70 (HQObjc`-[HQPerson .cxx_destruct] at HQPerson.m:11)
    }
    (lldb) p $0->get(6).big()
    Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/hq/study/OC底层原理/V1/objc/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    

    从lldb的结果看,当前的方法列表中只包含HQPerson中的实例方法.而分类中的方法并未合并到当前的方法列表中.那分类的方法是何时合并的呢?接着顺着断点单步调试.在load_images时,有loadAllCategories操作,这个函数就是用来加载所有分类的.loadAllCategories源码分析请看文末.在loadAllCategories函数中通过attachLists函数,将分类的方法与类的方法进行合并.

    接下来通过lldb查看经过attachLists操作前后的情况

    • attachLists操作前
    (lldb) p/x cls
    (Class) $0 = 0x00000001000083a0 HQPerson
    (lldb) p (class_data_bits_t*)(0x00000001000083a0+0x20)
    (class_data_bits_t *) $1 = 0x00000001000083c0
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000100704560
    (lldb) p $2->methods()
    (const method_array_t) $3 = {
      list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
         = {
          list = {
            ptr = 0x00000001000081d0
          }
          arrayAndFlag = 4295000528
        }
      }
    }
    //当前方法列表存储着主类的方法,共6个:3个实例方法,1个setter,1个getter,1个cxx
    (lldb) p $3.count()
    (uint32_t) $4 = 6
    (lldb) p $3.beginLists()
    (const method_list_t_authed_ptr<method_list_t> *) $5 = 0x00000001005eda60
    (lldb) p (method_list_t**)$5
    (method_list_t **) $6 = 0x00000001005eda60
    (lldb) p *$6
    (method_list_t *) $7 = 0x00000001000081d0
    (lldb) p $7->get(0).big()
    (method_t::big) $8 = {
      name = "instanceMethod1"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003ab0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $6+1
    (method_list_t **) $9 = 0x00000001005eda68
    (lldb) p *$9
    (method_list_t *) $10 = 0x0000000000000000
    
    • attachLists操作后
    (lldb) p $2->methods()
    (const method_array_t) $11 = {
      list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
         = {
          list = {
            ptr = 0x0000000100704711
          }
          arrayAndFlag = 4302325521
        }
      }
    }
    //此时方法列表中共有9个方法,分别是主类的6个方法,分类(HQA)的3个方法
    (lldb) p $11.count()
    (uint32_t) $12 = 9
    (lldb) p $11.beginLists()
    (const method_list_t_authed_ptr<method_list_t> *) $13 = 0x0000000100704718
    (lldb) p (method_list_t**)$13
    (method_list_t **) $14 = 0x0000000100704718
    (lldb) p *$14
    //从此处可以看出,存在方法列表中的第一个列表是存在分类(HQA)中的方法
    (method_list_t *) $15 = 0x0000000100008040
    (lldb) p $15->get(0).big()
    (method_t::big) $16 = {
      name = "instanceMethod1"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x00000001000039c0 (HQObjc`-[HQPerson(HQA) instanceMethod1] at HQPerson+HQA.m:21)
    }
    (lldb) p $14+1
    (method_list_t **) $17 = 0x0000000100704720
    (lldb) p *$17
    //从此处可以看出,存在方法列表中的第二个列表是存在主类中的方法
    (method_list_t *) $18 = 0x00000001000081d0
    (lldb) p $18->get(0).big()
    (method_t::big) $19 = {
      name = "instanceMethod1"
      types = 0x0000000100003f6e "v16@0:8"
      imp = 0x0000000100003ab0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $17+1
    (method_list_t **) $20 = 0x0000000100704728
    (lldb) p *$20
    //此时的地址已经不像方法列表的地址
    (method_list_t *) $21 = 0x0002000100605d40
    (lldb) p $21->get(0).big()
    error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).
    The process has been returned to the state before expression evaluation.
    

    总结
    * 当attachLists之前,当前类的方法列表中只存储了主类的方法列表,即methods()得到的方法列表中,存储的是主类方法列表的指针.
    * 当attachLists之后,当前类的方法列表中存储了分类(HQA)的方法列表和主类的方法列表,即methods()得到的方法列表中,存储了主类方法列表的指针和分类方法列表的指针.
    * 当attachLists完成,在methods()得到的方法列表中,分类的方法列表指针排在主类的方法列表指针之前,因此若分类和主类有同名函数,当进行消息查找时,找到分类方法后返回分类的方法而不会继续查找主类的方法,即,分类添加与主类同名的函数后,进行方法调用时,会调用分类的方法.
    * 当主类实现+load方法且分类也实现+load方法时,主类的方法列表是编译时期就确定了,但分类的方法列表不会在编译时期合并至主类的方法列表,而是以动态的通过runtime全并至主类,并将合并之后的方法列表存储在rwe中.

    主类未实现+load方法 + 分类实现+load方法

    主类未实现+load方法时,理论上类的初始化操作需要推迟至第一次方法调用时,但由于分类实现了+load方法,通过单步调试时发现,进入map_images -> realizeClassWithoutSwift-> methodizeClass流程,这也是主类实现了+load方法非懒加载流程.

    通过堆栈信息验证,当主类未实现+load方法,分类实现+load方法时,类的加载流程是非懒加载形式,其堆栈信息如下:

    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    * frame #0: 0x00000001002f6965 libobjc.A.dylib`realizeClassWithoutSwift(cls=HQPerson, previously=0x0000000000000000) at objc-runtime-new.mm:2659:57
    frame #1: 0x00000001002d41d6 libobjc.A.dylib`_read_images(hList=0x00007ffeefbf36d0, hCount=261, totalClasses=22057, unoptimizedTotalClasses=22057) at objc-runtime-new.mm:3835:13
    frame #2: 0x00000001002d3008 libobjc.A.dylib`map_images_nolock(mhCount=261, mhPaths=0x00007ffeefbf4e00, mhdrs=0x00007ffeefbf4060) at objc-os.mm:596:9
    frame #3: 0x00000001002d2b63 libobjc.A.dylib`map_images(count=261, paths=0x00007ffeefbf4e00, mhdrs=0x00007ffeefbf4060) at objc-runtime-new.mm:3165:12
    frame #4: 0x0000000100019668 dyld`dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), bool, bool) + 1749
    frame #5: 0x0000000100019809 dyld`dyld::registerObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*)) + 63
    frame #6: 0x00007fff6f1d3ad1 libdyld.dylib`_dyld_objc_notify_register + 113
    frame #7: 0x00000001002d261a libobjc.A.dylib`_objc_init at objc-os.mm:939:5
    frame #8: 0x000000010044f0bc libdispatch.dylib`_os_object_init + 13
    frame #9: 0x000000010045fafc libdispatch.dylib`libdispatch_init + 282
    frame #10: 0x00007fff6c144791 libSystem.B.dylib`libSystem_initializer + 220
    frame #11: 0x000000010002f1d3 dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 535
    frame #12: 0x000000010002f5de dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
    frame #13: 0x0000000100029ffb dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 493
    frame #14: 0x0000000100029f66 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 344
    frame #15: 0x00000001000280b4 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #16: 0x0000000100028154 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #17: 0x0000000100016662 dyld`dyld::initializeMainExecutable() + 129
    frame #18: 0x000000010001bbba dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 6667
    frame #19: 0x0000000100015227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #20: 0x0000000100015025 dyld`_dyld_start + 37
    

    此时通过lldb查看类的方法列表中的数据如下:

    method_list_t *list = ro->baseMethods();
    
    (lldb) p list
    (method_list_t *) $0 = 0x0000000100008078
    (lldb) p $0->get(0).big()
    (method_t::big) $1 = {
      name = "instanceHQBMethod2"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003bd0 (HQObjc`-[HQPerson(HQB) instanceHQBMethod2] at HQPerson+HQB.m:16)
    }
    (lldb) p $0->get(1).big()
    (method_t::big) $2 = {
      name = "instanceMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003c00 (HQObjc`-[HQPerson(HQB) instanceMethod1] at HQPerson+HQB.m:20)
    }
    (lldb) p $0->get(2).big()
    (method_t::big) $3 = {
      name = "instanceHQBMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003c30 (HQObjc`-[HQPerson(HQB) instanceHQBMethod1] at HQPerson+HQB.m:24)
    }
    (lldb) p $0->get(3).big()
    (method_t::big) $4 = {
      name = "instanceHQAMethod2"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x00000001000039e0 (HQObjc`-[HQPerson(HQA) instanceHQAMethod2] at HQPerson+HQA.m:17)
    }
    (lldb) p $0->get(4).big()
    (method_t::big) $5 = {
      name = "instanceMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003a10 (HQObjc`-[HQPerson(HQA) instanceMethod1] at HQPerson+HQA.m:21)
    }
    (lldb) p $0->get(5).big()
    (method_t::big) $6 = {
      name = "instanceHQAMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003a40 (HQObjc`-[HQPerson(HQA) instanceHQAMethod1] at HQPerson+HQA.m:25)
    }
    (lldb) p $0->get(6).big()
    (method_t::big) $7 = {
      name = "instancePersonMethod2"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003aa0 (HQObjc`-[HQPerson instancePersonMethod2] at HQPerson.m:17)
    }
    (lldb) p $0->get(7).big()
    (method_t::big) $8 = {
      name = "instanceMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003ad0 (HQObjc`-[HQPerson instanceMethod1] at HQPerson.m:21)
    }
    (lldb) p $0->get(8).big()
    (method_t::big) $9 = {
      name = "instancePersonMethod1"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003b00 (HQObjc`-[HQPerson instancePersonMethod1] at HQPerson.m:25)
    }
    (lldb) p $0->get(9).big()
    (method_t::big) $10 = {
      name = "name"
      types = 0x0000000100003f91 "@16@0:8"
      imp = 0x0000000100003b30 (HQObjc`-[HQPerson name] at HQPerson.h:18)
    }
    (lldb) p $0->get(10).big()
    (method_t::big) $11 = {
      name = "setName:"
      types = 0x0000000100003f99 "v24@0:8@16"
      imp = 0x0000000100003b60 (HQObjc`-[HQPerson setName:] at HQPerson.h:18)
    }
    (lldb) p $0->get(11).big()
    (method_t::big) $12 = {
      name = ".cxx_destruct"
      types = 0x0000000100003f7d "v16@0:8"
      imp = 0x0000000100003b90 (HQObjc`-[HQPerson .cxx_destruct] at HQPerson.m:11)
    }
    (lldb) p $0->get(12).big()
    Assertion failed: (i < count), function get, file /Users/macbookpro/Desktop/hq/study/OC底层原理/V1/objc/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    

    从lldb的结果看,类的实例方法/分类HQA/分类HQB的方法,也是在编译时期已经合并.

    总结

    • 类的初始化分为懒加载(实现+load方法)非懒加载(未实现+load方法)两种形式.

    • 分类的方法合并至主类的方法列表分为编译时合并通过runtime动态合并.

    • 当类与分类是否实现+load时,方法列表的合并有4种情况:

      • 主类实现+load方法 + 分类未实现+load方法时,主类是非懒加载的,分类的方法是在编译时期已经与主类的方法进行合并.
      • 主类未实现+load方法 + 分类未实现+load方法时,主类是懒加载的,分类的方法也是在编译时期已经与主类的方法进行合并.
      • 主类实现+load方法 + 分类实现+load方法时,主类非懒加载的,分类的方法是通过runtime动态的与主类方法进行合并.
      • 主类未实现+load方法 + 分类实现+load方法时,主类转换成非懒加载形式,分类的方法也是在编译时期与主类的方法进行合并.
    • 分类的方法列表在编译时期合并时,当使用methods()方法获取类的方法列表时,返回的结果中只有一个method_list_t指针,该指针指向的方法method_t全部进行SEL大小排序.
      如:HQPerson(HQA) 方法1 < HQPerson(HQB) 方法1< HQPerson 方法1 < HQPerson(HQA) 方法2 < HQPerson(HQB) 方法2<HQPerson 方法2.

    • 分类的方法列表通过runtime合并时,当使用methods()方法获取类的方法列表时,返回的结果中有三个method_list_t指针,每个method_list_t指针指向的方法method_t进行独立的SEL大小排序.
      如:
      HQPerson(HQA) 方法1 < HQPerson(HQA) 方法2 ;
      HQPerson(HQB) 方法1 < HQPerson(HQB) 方法2 ;
      HQPerson 方法1 < < HQPerson 方法2 .
      而方法列表是按编译的顺序的逆序进行排列,先编译的在列表的尾部,后编译的在列表的前面.
      如:
      methods()->beginLists()[0] = HQPerson(HQB)的method_list_t
      methods()->beginLists()[1] = HQPerson(HQA) 的method_list_t
      methods()->beginLists()[2] = HQPerson的method_list_t

    分类中通过关联对象的方式添加属性

    首先,验证是否能在分类中定义catName的属性.
    在分类中添加catName的属性,在main函数中对其进行赋值.在编译的时候并未报错,这说明,是可以在分类中定义属性的.

    其次,验证是否能在main函数中对catName属性进行赋值.
    在main函数中调用 person.catName = @"123"; 结果执行崩溃,错误信息如下:

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HQPerson setCatName:]: unrecognized selector sent to instance 0x100645d60'
    

    从错误提示中可知,是未找到setCatName的实例方法.这也验证了,分类中定义属性时,不会为该属性添加getter/setter方法.
    既然分类没有自动添加getter/setter方法,那是不是可以手动添加getter/setter方法呢?答案是可以的,因为OC是动态语言,方法真正的实现是通过runtime完成的,既然这样那手动添加试试.

    在HQPerson+HQA.m中添加catName的getter/setter方法

    //运行时实现setter方法
    - (void)setCatName:(NSString *)catName{
        objc_setAssociatedObject(self, "catName", catName, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    //运行时实现getter方法
    - (NSString *)catName{
        return objc_getAssociatedObject(self, "catName");
    }
    

    增加关联对象后效果如下:

    person.catName = @"123";    //执行setter成功
    NSLog(@"catName:%@",person.catName);    //执行getter成功,取出的值为@"123"
    

    经过验证,在分类中可以添加属性,在分类的结构体中,使用instanceProperties指向了分类的属性列表.但分类不会为属性添加getter/setter方法和私有成员变量.如果需要使用分类中的属性,则必须手动添加getter/setter方法,可通过关联对象的方法或者使用@dynamic方法.

    loadAllCategories函数源码分析

    • 1:查看loadAllCategories源码如下:
    static void loadAllCategories() {
        mutex_locker_t lock(runtimeLock);
    
        for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
            load_categories_nolock(hi);
        }
    }
    
    • 2:进入load_categories_nolock函数
    static void load_categories_nolock(header_info *hi) {
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
        size_t count;
        auto processCatlist = [&](category_t * const *catlist) {
            for (unsigned i = 0; i < count; i++) {
                category_t *cat = catlist[i];
                Class cls = remapClass(cat->cls);
                locstamped_category_t lc{cat, hi};
    
                if (!cls) {
                    // Category's target class is missing (probably weak-linked).
                    // Ignore the category.
                    if (PrintConnecting) {
                        _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                     "missing weak-linked target class",
                                     cat->name, cat);
                    }
                    continue;
                }
    
                // Process this category.
                if (cls->isStubClass()) {
                    // Stub classes are never realized. Stub classes
                    // don't know their metaclass until they're
                    // initialized, so we have to add categories with
                    // class methods or properties to the stub itself.
                    // methodizeClass() will find them and add them to
                    // the metaclass as appropriate.
                    if (cat->instanceMethods ||
                        cat->protocols ||
                        cat->instanceProperties ||
                        cat->classMethods ||
                        cat->protocols ||
                        (hasClassProperties && cat->_classProperties))
                    {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                } else {
                    // First, register the category with its target class.
                    // Then, rebuild the class's method lists (etc) if
                    // the class is realized.
                    if (cat->instanceMethods ||  cat->protocols
                        ||  cat->instanceProperties)
                    {
                        if (cls->isRealized()) {
                            attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls);
                        }
                    }
    
                    if (cat->classMethods  ||  cat->protocols
                        ||  (hasClassProperties && cat->_classProperties))
                    {
                        if (cls->ISA()->isRealized()) {
                            attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls->ISA());
                        }
                    }
                }
            }
        };
    
        //处理__objc_catlist记录的分类
        processCatlist(hi->catlist(&count));
        //处理__objc_catlist2记录的分类
        processCatlist(hi->catlist2(&count));
    }
    
    • 2.1:通过_getObjc2CategoryList函数,获取MachO文件中分类的个数以及分类列表的地址.注意:当分类未实现+load方法时,此列表中不会存储分类的信息.
    processCatlist(hi->catlist(&count));
    category_t * const *header_info::catlist(size_t *outCount) const
    {
        // This field is new, so temporarily be resilient to the shared cache
        // not generating it
        if (isPreoptimized() && hasPreoptimizedSectionLookups()) {
          *outCount = catlist_count;
          category_t * const *list = (category_t * const *)(((intptr_t)&catlist_offset) + catlist_offset);
          return list;
        }
        //从MachO文件中获取__objc_catlist段数据,__objc_catlist段中数据表示MachO中记录的分类
        return _getObjc2CategoryList(mhdr(), outCount);
    }
    
    GETSECT(_getObjc2CategoryList, category_t * const, "__objc_catlist");//记录了所使用的分类
    
    • 2.2:遍历分类列表,依次对分类列表中的分类进行处理attachCategories.
    static void
    attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags)
    {
        //无关代码省略
        constexpr uint32_t ATTACH_BUFSIZ = 64;
        method_list_t   *mlists[ATTACH_BUFSIZ];
        property_list_t *proplists[ATTACH_BUFSIZ];
        protocol_list_t *protolists[ATTACH_BUFSIZ];
    
        uint32_t mcount = 0;
        uint32_t propcount = 0;
        uint32_t protocount = 0;
        bool fromBundle = NO;
        bool isMeta = (flags & ATTACH_METACLASS);
        //开辟rwe,并将类的基本方法赋值给rwe
        auto rwe = cls->data()->extAllocIfNeeded();
        for (uint32_t i = 0; i < cats_count; i++) {
            auto& entry = cats_list[i];
            //获取分类中的方法列表
            method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
            if (mlist) {
                if (mcount == ATTACH_BUFSIZ) {
                    prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
                    rwe->methods.attachLists(mlists, mcount);
                    mcount = 0;
                }
                mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
                fromBundle |= entry.hi->isBundle();
            }
    
            property_list_t *proplist =
                entry.cat->propertiesForMeta(isMeta, entry.hi);
            if (proplist) {
                if (propcount == ATTACH_BUFSIZ) {
                    rwe->properties.attachLists(proplists, propcount);
                    propcount = 0;
                }
                proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
            }
            protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
            if (protolist) {
                if (protocount == ATTACH_BUFSIZ) {
                    rwe->protocols.attachLists(protolists, protocount);
                    protocount = 0;
                }
                protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
            }
        }
    
        if (mcount > 0) {
            //对分类方法列表中的方法进行排序,排序方法按SEL的由小到大顺序
            prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                               NO, fromBundle, __func__);
            //将排序后的分类方法合并至rwe中.此时rwe是一个二维数组,并且,二维数组的是按逆序插入的,即在rwe数组中分类的方法列表排在原有类的方法列表前面
            rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
            if (flags & ATTACH_EXISTING) {
                flushCaches(cls, __func__, [](Class c){
                    // constant caches have been dealt with in prepareMethodLists
                    // if the class still is constant here, it's fine to keep
                    return !c->cache.isConstantOptimizedCache();
                });
            }
        }
        //分类中的属性处理
        rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
        //分类中的协议处理
        rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
    }
    
    • 2.3:由于类的ro编译时已经确定,并且不能在运行时进行修改,因此在attachCategories函数中,开辟rwe,并将ro->baseMethods通过rwe->methods.attachLists函数赋值给rwe.注意:此时rwe是一个一维数组
    class_rw_ext_t *
    class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
    {
        runtimeLock.assertLocked();
    
        //开辟rwe的内存空间
        auto rwe = objc::zalloc<class_rw_ext_t>();
    
        rwe->version = (ro->flags & RO_META) ? 7 : 0;
    
        method_list_t *list = ro->baseMethods();
        if (list) {
            if (deepCopy) list = list->duplicate();
            //将原有类的方法列表赋值给rwe
            rwe->methods.attachLists(&list, 1);
        }
    
        property_list_t *proplist = ro->baseProperties;
        if (proplist) {
            //将原有类的属性列表赋值给rwe
            rwe->properties.attachLists(&proplist, 1);
        }
    
        protocol_list_t *protolist = ro->baseProtocols;
        if (protolist) {
            //将原有类的协议列表赋值给rwe
            rwe->protocols.attachLists(&protolist, 1);
        }
        set_ro_or_rwe(rwe, ro);
        return rwe;
    }
    
    • 2.4:遍历分类的方法列表,将分类的方法列表通过rwe->methods.attachLists方法附着到主类的rwe中.
    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
    
        if (hasArray()) {
            // many lists -> many lists
            //获取旧的lists个数
            uint32_t oldCount = array()->count;
            //加上当前需要添加的list的个数,得到新的lists的大小
            uint32_t newCount = oldCount + addedCount;
            //开辟newCount个大小的数组,用于存储当前所有的list
            array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
            newArray->count = newCount;
            array()->count = newCount;
    
            for (int i = oldCount - 1; i >= 0; i--)
                //将旧的lists中的数据按原顺序添加至新lists的尾部
                newArray->lists[i + addedCount] = array()->lists[i];
            for (unsigned i = 0; i < addedCount; i++)
                //将新lists中的数据从头开始添加至新lists中
                newArray->lists[i] = addedLists[i];
            free(array());
            setArray(newArray);
            validate();
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            //由于此时lists数组中为空,此时直接将addedLists[0]赋值给lists
            list = addedLists[0];
            validate();
        } 
        else {
            // 1 list -> many lists
            Ptr<List> oldList = list;
            //得到当前list的数量
            uint32_t oldCount = oldList ? 1 : 0;
            //计算容量和 = 旧list个数+新lists的个数
            uint32_t newCount = oldCount + addedCount;
            //开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到array中,通过array()获取
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            //设置数组的大小
            array()->count = newCount;
            //将旧的list放入到新数组的末尾
            if (oldList) array()->lists[addedCount] = oldList;
            // memcpy(开始位置,放什么,放多大) 是内存平移,从数组起始位置存入新的list
            //其中array()->lists 表示首位元素位置
            for (unsigned i = 0; i < addedCount; i++)
                array()->lists[i] = addedLists[i];
            validate();
        }
    }
    

    loadAllCategories函数方法合并总结

    • 开辟rwe,并将原有类的方法列表赋值给rwe.此时rwe是一个指针,指向原有类的方法列表.
    • 遍历每个分类,获取分类中的方法列表,注意:一个类最多有64个分类
      • 第一次获取到分类的方法列表时,此时rwe中已经有方法列表,创建一个大小为2的数组.将旧的方法列表放到数组的末尾,将新的方法列表从数组的起始位置存入数组中.此时rwe是一个数组.
      • rwe是以数组的形式存放方法列表时,则根据当前分类中方法列表的个数,扩大rwe的数组,将rwe原数组中内容放置新数组的末尾,将新的方法列表从新数组的起始位置存入数组中.

    相关文章

      网友评论

          本文标题:OC底层原理14 - 类的加载之分类

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