您的位置:首页 > 移动开发 > IOS开发

iOS 分类的加载

2020-04-20 15:02 1056 查看

iOS 分类的加载

  • 3.  分类 Category 的加载
  • 总结
  • 前言

      通过上一篇对类加载的分析探索,我们了解了

    dyld
    进入程序,加载完镜像文件,在
    objc_init
    方法中注册回调函数,然后通过
    map_images
    的一系列操作,将其加载到内存中。
    map_images
    中,通过
    _read_images
    方法,先创建表,遍历所有的类将其映射到表中,然后将
    SEL
    协议
    添加到对应的表中,对
    类和非懒加载类
    进行初始化,对
    ro
    rw
    赋值等等一系列流程。那么今天我们先来看几道关于
    ro
    rw
    的面试题,然后再了解一下
    类和非懒加载类
    以及
    分类Category
    的加载。

    1.  Runtime 面试题

    问 :可否给类动态添加成员变量?为什么?

    答 :动态创建的类,可以添加成员变量,已经注册好的类,不能动态添加成员变量。

    分析如下:

    首先,我们使用

    Runtime API
    编写下面代码:

    // 1: 动态创建类
    Class LGPerson = objc_allocateClassPair([NSObject class], "LGPerson", 0);
    // 2: 添加成员变量
    // ivar - ro - ivarlist
    class_addIvar(LGPerson, "lgName", sizeof(NSString *), log2(sizeof(NSString *)), "@");
    // 3: 注册到内存
    objc_registerClassPair(LGPerson);

    通过上面代码进行 动态创建类添加成员变量、然后注册到内存,运行代码,程序可以正常运行。

    当我们对 步骤二步骤三 顺序互换,先注册到内存,再添加成员变量,此时,程序就会崩溃,接下来通过源码来分析一下。

    通过之前的学习,知道,成员变量是存储在

    Class
    class_rw_t *data()
    中的
    ro
    中的
    ivar_list_t * ivars
    里面。如下源码:

    objc_class
    源码

    struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    class_rw_t *data() {
    return bits.data();
    }
    ...
    }

    class_rw_t
    源码:

    struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;
    
    const class_ro_t *ro;
    
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    ...
    ...
    }

    class_ro_t
    源码:

    struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    #ifdef __LP64__
    uint32_t reserved;
    #endif
    
    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    ...
    ...
    }

    通过上一篇的学习,我们知道

    ro
    中在程序编译时就进行赋值的,只能读取,不能进行改变,
    rw
    是在对类初始化时,进行赋值的。而
    rw—>ro
    的赋值也是在这个时候完成。

    我们查看

    Runtime
    将注册类的
    API
    objc_registerClassPair
    ,源码如下:

    /***********************************************************************
    * objc_registerClassPair
    * fixme
    * Locking: acquires runtimeLock
    **********************************************************************/
    void objc_registerClassPair(Class cls)
    {
    mutex_locker_t lock(runtimeLock);
    
    checkIsKnownClass(cls);
    
    if ((cls->data()->flags & RW_CONSTRUCTED)  ||
    (cls->ISA()->data()->flags & RW_CONSTRUCTED))
    {
    _objc_inform("objc_registerClassPair: class '%s' was already "
    "registered!", cls->data()->ro->name);
    return;
    }
    
    if (!(cls->data()->flags & RW_CONSTRUCTING)  ||
    !(cls->ISA()->data()->flags & RW_CONSTRUCTING))
    {
    _objc_inform("objc_registerClassPair: class '%s' was not "
    "allocated with objc_allocateClassPair!",
    cls->data()->ro->name);
    return;
    }
    
    // Clear "under construction" bit, set "done constructing" bit
    // 替换
    cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
    cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
    // Add to named class table.
    addNamedClass(cls, cls->data()->ro->name);
    }

    其中关键步骤

    cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
    cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);

    在注册时,将

    RW_CONSTRUCTING | RW_REALIZING
    替换为
    RW_CONSTRUCTED

    通过查看动态添加成员变量

    class_addIvar API

    BOOL
    class_addIvar(Class cls, const char *name, size_t size,
    uint8_t alignment, const char *type)
    {
    if (!cls) return NO;
    
    if (!type) type = "";
    if (name  &&  0 == strcmp(name, "")) name = nil;
    
    mutex_locker_t lock(runtimeLock);
    
    checkIsKnownClass(cls);
    assert(cls->isRealized());
    
    // No class variables
    if (cls->isMetaClass()) {
    return NO;
    }
    
    // Can only add ivars to in-construction classes.
    if (!(cls->data()->flags & RW_CONSTRUCTING)) {
    return NO;
    }
    
    // Check for existing ivar with this name, unless it's anonymous.
    // Check for too-big ivar.
    // fixme check for superclass ivar too?
    if ((name  &&  getIvar(cls, name))  ||  size > UINT32_MAX) {
    return NO;
    }
    
    class_ro_t *ro_w = make_ro_writeable(cls->data());
    
    // fixme allocate less memory here
    
    ivar_list_t *oldlist, *newlist;
    if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) {
    size_t oldsize = oldlist->byteSize();
    newlist = (ivar_list_t *)calloc(oldsize + oldlist->entsize(), 1);
    memcpy(newlist, oldlist, oldsize);
    free(oldlist);
    } else {
    newlist = (ivar_list_t *)calloc(sizeof(ivar_list_t), 1);
    newlist->entsizeAndFlags = (uint32_t)sizeof(ivar_t);
    }
    
    uint32_t offset = cls->unalignedInstanceSize();
    uint32_t alignMask = (1<<alignment)-1;
    offset = (offset + alignMask) & ~alignMask;
    
    ivar_t& ivar = newlist->get(newlist->count++);
    #if __x86_64__
    // Deliberately over-allocate the ivar offset variable.
    // Use calloc() to clear all 64 bits. See the note in struct ivar_t.
    ivar.offset = (int32_t *)(int64_t *)calloc(sizeof(int64_t), 1);
    #else
    ivar.offset = (int32_t *)malloc(sizeof(int32_t));
    #endif
    *ivar.offset = offset;
    ivar.name = name ? strdupIfMutable(name) : nil;
    ivar.type = strdupIfMutable(type);
    ivar.alignment_raw = alignment;
    ivar.size = (uint32_t)size;
    
    ro_w->ivars = newlist;
    cls->setInstanceSize((uint32_t)(offset + size));
    
    // Ivar layout updated in registerClass.
    
    return YES;
    }

    看上面源码中的关键代码,

    // Can only add ivars to in-construction classes.
    if (!(cls->data()->flags & RW_CONSTRUCTING)) {
    return NO;
    }

    当我们在注册类时,对

    cls->ISA()->changeInfo
    cls->changeInfo
    进行修改,所以在上面判断中直接返回
    NO
    ,而不会在后续对
    ro
    中的
    ivar
    赋值。所以不能对注册好的类,进行动态添加成员变量。

    那么接下来对

    LGPerson
    类,添加属性,并打印,如下代码,问能否打印?为什么?

    定义如下方法:

    void lg_class_addProperty(Class targetClass , const char *propertyName){
    
    objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
    objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
    objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
    objc_property_attribute_t backingivar  = { "V", [NSString stringWithFormat:@"_%@",[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]].UTF8String };  //variable name
    objc_property_attribute_t attrs[] = {type, ownership0, ownership,backingivar};
    
    class_addProperty(targetClass, propertyName, attrs, 4);
    
    }

    添加属性到

    rw

    // 添加property - rw
    lg_class_addProperty(LGPerson, "subject");
    
    [person setValue:@"master" forKey:@"subject"];
    NSLog(@"%@",[person valueForKey:@"subject"]);

    答案:不能打印,我们只添加了

    subject
    属性到
    rw
    ,由于是我们动态添加的,系统并未生成
    setter
    getter
    ,而赋值打印相当于调用了这两个方法,所以不能打印。

    我们需要添加如下代码,将

    setter
    getter
    添加到方法列表中。

    添加setter  +  getter 方法
    class_addMethod(LGPerson, @selector(setSubject:), (IMP)lgSetter, "v@:@");
    class_addMethod(LGPerson, @selector(subject), (IMP)lgName, "@@:");

    小结:

    1. 动态创建的类,可以动态添加成员变量和属性,而已经创建好注册好的类,不能动态添加成员变量。
    2. 动态添加的属性,默认不会生成 setter 和 getter,需要将setter  +  getter
    方法添加到方法列表中。

    2.  类和非懒加载类的加载

    2.1  类和非懒加载类分析

    通过上一篇的学习得知,在

    _read_images()
    方法中,在将类添加到表中之后,会进行一系列的操作,比如:将
    SEL
    注册到哈希表中、将
    协议
    添加到表中、初始化非懒加载类、初始化懒加载类、处理分类
    category
    等。

    那么什么是懒加载类,什么是非懒加载类呢?

    接下来,先创建

    LGTeacher
    LGStudent
    LGPerson
    三个类,在其中前两个类中,实现如下
    load
    方法:

    +(void)load
    {
    NSLog(@"%s",__func__);
    }

    然后在

    _read_images()
    方法中,初始化
    非懒加载类
    的时候,打印类信息,如下

    然后运行代码,查看控制台,

    只打印了有

    load
    方法的两个类,并未找到没有实现
    load
    方法的
    LGPerson
    ,而
    LGPerson
    是在
    main
    函数里进行了调用,实例出一个对象。

    由此:

    load
    方法会将类的加载提前,将类的编译期提前到加载数据的地方。而实现了
    load
    方法的类,就是非懒加载类。而懒加载类是当你用到此类的时候再进行加载实现。

    接下来,我们分析一下 非懒加载类的加载懒加载类的加载

    2.2  非懒加载类的加载

    在上一篇中分析中得知,当进入

    _read_images()
    方法中的非懒加载类的加载步骤后,流程如下:

    • 获取所有
      非懒加载类
      classref_t *classlist = _getObjc2NonlazyClassList(hi, &count)
    • 循环读取
      非懒加载类
      ,将其加到内存,
      addClassTableEntry(cls)
    • 实现所有
      非懒加载类
      (实例化类对象的一些信息,例如rw),
      realizeClassWithoutSwift(cls)
    • realizeClassWithoutSwift(cls)
      中,对
      cls
      supClass
      isa
      以及
      rw->ro
      等进行赋值,然后进入
      methodizeClass(cls)
      ,用
      ro
      中的数据对
      rw
      进行赋值。

    rw->ro
    的赋值

    2.3  懒加载类的加载

    懒加载类是在调用的时候才会加载,那么我们在

    main
    函数中,创建
    LGPerson
    ,然后打下断点,进行分析。

    当我们调用

    alloc
    方法时,会进入方法查找流程,必然会进入
    lookUpImpOrForward
    方法。

    然后判断进入

    realizeClassMaybeSwiftAndLeaveLocked
    方法,

    查看

    realizeClassMaybeSwiftAndLeaveLocked
    方法源码:

    static Class
    realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
    {
    return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
    }

    realizeClassMaybeSwiftAndLeaveLocked()
    调用
    realizeClassMaybeSwiftMaybeRelock()
    方法,源码如下:

    /***********************************************************************
    * realizeClassMaybeSwift (MaybeRelock / AndUnlock / AndLeaveLocked)
    * Realize a class that might be a Swift class.
    * Returns the real class structure for the class.
    * Locking:
    *   runtimeLock must be held on entry
    *   runtimeLock may be dropped during execution
    *   ...AndUnlock function leaves runtimeLock unlocked on exit
    *   ...AndLeaveLocked re-acquires runtimeLock if it was dropped
    * This complication avoids repeated lock transitions in some cases.
    **********************************************************************/
    static Class
    realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
    {
    lock.assertLocked();
    // 判断是否是Swift
    if (!cls->isSwiftStable_ButAllowLegacyForNow()) { // 否
    // Non-Swift class. Realize it now with the lock still held.
    // fixme wrong in the future for objc subclasses of swift classes
    
    // 初始化,
    realizeClassWithoutSwift(cls);
    if (!leaveLocked) lock.unlock();
    } else {
    // Swift class. We need to drop locks and call the Swift
    // runtime to initialize it.
    lock.unlock();
    cls = realizeSwiftClass(cls);
    assert(cls->isRealized());    // callback must have provoked realization
    if (leaveLocked) lock.lock();
    }
    
    return cls;
    }

    通过上面

    realizeClassMaybeSwiftMaybeRelock
    源码分析,先判断是否是
    Swift
    ,不是,则调用
    realizeClassWithoutSwift(cls)
    对类初始化,对
    superClass
    isa
    rw
    赋值,和上面的
    非懒加载类
    一样。

    当类初始化完成后,会进入

    lookUpImpOrForward
    的方法查找流程,然后进行初始化,分配空间等等的操作。

    注意:
    当一个类继承另一个类的时候,子类实现了

    load
    方法,子类变成了
    非懒加载类
    ,而父类也会变成
    非懒加载类
    。通过
    realizeClassWithoutSwift
    方法中的下面的代码,递归对
    supercls
    初始化。

    3.  分类 Category 的加载

    首先,我们创建一个

    LGTeacher
    类和
    LGTeacher (test)
    分类如下:

    @interface LGTeacher : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *subject;
    @property (nonatomic, assign) int age;
    
    - (void)sayHello;
    
    + (void)sayMaster;
    @end
    
    #import "LGTeacher.h"
    
    @implementation LGTeacher
    
    //+ (void)load{
    //    NSLog(@"%s",__func__);
    //}
    @end
    
    @interface LGTeacher (test)
    @property (nonatomic, copy) NSString *cate_p1;
    @property (nonatomic, copy) NSString *cate_p2;
    
    - (void)cate_instanceMethod1;
    - (void)cate_instanceMethod2;
    
    + (void)cate_classMethod1;
    + (void)cate_classMethod2;
    
    @end
    
    @implementation LGTeacher (test)
    
    //+ (void)load{
    //    NSLog(@"分类 load");
    //}
    
    - (void)setCate_p1:(NSString *)cate_p1{
    }
    
    - (NSString *)cate_p1{
    return @"cate_p1";
    }
    
    - (void)cate_instanceMethod2{
    NSLog(@"%s",__func__);
    }
    
    + (void)cate_classMethod2{
    NSLog(@"%s",__func__);
    }
    @end

    3.1 clang 初探 分类 Category 的结构

    通过

    clang
    ,查看
    c++
    文件,

    _category_t
    的结构:

    // attachlist 方法 对象 C++
    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;
    };
    // OC
    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);
    };

    为什么会有两个方法列表呢?
    对象方法存在类中,类方法存在元类中。当通过

    attachLists
    方法添加方法时,需要添加到不同的类中。

    3.2 类 与 分类 Category 的搭配加载

    1. 懒加载的分类(未实现
    load
    方法)

    我们知道在

    read_iamge
    方法中,在类初始化时,是通过在
    methodizeClass
    方法中,用
    ro
    rw
    进行赋值的,通过
    attachCategories
    方法,将分类中的方法添加到方法列表中的。

    通过断点调试,判断为

    LGTeacher
    时,


    category_list
    NULL
    ,当为
    NULL
    时,
    attachCategories
    直接返回,

    那么分类的方法是否添加呢?我们通过

    lldb
    分析一下:

    methods
    里面有值,

    打印
    methods
    里面的方法,

    我们发现,方法列表中已经有分类的方法,那么说明,懒加载的分类的加载时在编译时处理的,不需要添加到表中,直接添加到相应

    data()
    ro
    里面,然后在初始化类的时候,直接用
    ro->baseMethods()
    rw->methods
    赋值。即下面的代码

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
    prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
    rw->methods.attachLists(&list, 1);
    }

    那么 懒加载的分类(未实现

    load
    方法) 搭配 懒加载类非懒加载类 时,又会出现两中情况。

    上面说的是搭配 懒加载类 的情况,而两者的

    分类的加载
    都是一样的,是在编译期进行处理,直接添加到相应
    data()
    ro
    中,主要的区别就是在对类的加载,上面的类和非懒加载类的加载已经说过,
    懒加载类
    是在发送消息时,通过
    lookuporforward
    ->
    realizeClassWithoutSwift
    ->
    methodlizeClass
    的流程加载的。而
    非懒加载类
    是通过
    read_images
    ->
    realizeClassWithoutSwift
    ->
    methodlizeClass
    的流程加载的.

    2. 非懒加载的分类(实现
    load
    方法)

    分类
    实现
    load
    方法时,其加载也会被提前,即
    read_iamges
    方法中对分类的处理,如下关键代码:

    for (i = 0; i < count; i++) {
    category_t *cat = catlist[i];
    Class cls = remapClass(cat->cls);
    
    if (!cls) {
    // Category's target class is missing (probably weak-linked).
    // Disavow any knowledge of this category.
    catlist[i] = nil;
    if (PrintConnecting) {  }
    continue;
    }
    
    // Process this category.
    // First, register the category with its target class.
    // Then, rebuild the class's method lists (etc) if
    // the class is realized.
    bool classExists = NO;
    // ✅判断是否是对象方法
    if (cat->instanceMethods ||  cat->protocols
    ||  cat->instanceProperties)
    {
    // ✅为类添加未附加的类别
    addUnattachedCategoryForClass(cat, cls, hi);
    const char *cname = cls->demangledName();
    const char *oname = "LGTeacher";
    if (cname && (strcmp(cname, oname) == 0)) {
    printf("_getObjc2CategoryList :%s \n",cname);
    }
    // ✅为判断是否是懒加载类
    if (cls->isRealized()) {
    remethodizeClass(cls);
    classExists = YES;
    }
    if (PrintConnecting) { }
    }
    // // ✅判断是否是类方法
    if (cat->classMethods  ||  cat->protocols
    ||  (hasClassProperties && cat->_classProperties))
    {
    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
    // ✅为判断是否是懒加载类
    if (cls->ISA()->isRealized()) {
    remethodizeClass(cls->ISA());
    }
    if (PrintConnecting) {   }
    }
    }
    }

    先读取分类,判断分类中的方法是否是对象方法,然后

    addUnattachedCategoryForClass
    为类添加未附加的类别。然后判断是否是懒加载类,不是
    懒加载类
    则调用
    remethodizeClass
    方法,将分类方法添加的主类的方法列表中

    remethodizeClass
    方法源码:

    static void remethodizeClass(Class cls)
    {
    category_list *cats;
    bool isMeta;
    
    runtimeLock.assertLocked();
    
    isMeta = cls->isMetaClass();
    
    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
    if (PrintConnecting) {
    _objc_inform("CLASS: attaching categories to class '%s' %s",
    cls->nameForLogging(), isMeta ? "(meta)" : "");
    }
    //✅添加分类方法
    attachCategories(cls, cats, true /*flush caches*/);
    free(cats);
    }
    }

    懒加载类
    的加载是在发送消息时,通过
    lookuporforward
    ->
    realizeClassWithoutSwift
    ->
    methodlizeClass
    的流程加载的。

    所以,当搭配

    非懒加载类
    的时候,会进入
    remethodizeClass
    方法,进而调用
    attachCategories()
    方法,将分类的方法贴到主类里面。

    那么当

    非懒加载分类
    搭配
    懒加载类
    的时候,此时就会出现,分类已经加载,而主类还未加载。分类的方法不知要添加到哪里主类里面去,那这样的分类提前加载是不是没有意义呢?

    当然不是,那么接下来,系统会调用下面方法,


    最终进入到

    prepare_load_methods
    方法中,然后调用
    realizeClassWithoutSwift
    方法,源码如下:

    void prepare_load_methods(const headerType *mhdr)
    {
    size_t count, i;
    
    runtimeLock.assertLocked();
    
    classref_t *classlist =
    _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
    schedule_class_load(remapClass(classlist[i]));
    }
    // map_images 完毕了
    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
    category_t *cat = categorylist[i];
    Class cls = remapClass(cat->cls);
    
    const class_ro_t *ro = (const class_ro_t *)cls->data();
    const char *cname = ro->name;
    const char *oname = "LGTeacher";
    if (strcmp(cname, oname) == 0) {
    printf("prepare_load_methods :%s \n",cname);
    }
    
    if (!cls) continue;  // category for ignored weak-linked class
    if (cls->isSwiftStable()) {
    _objc_fatal("Swift class extensions and categories on Swift "
    "classes are not allowed to have +load methods");
    }
    realizeClassWithoutSwift(cls);
    assert(cls->ISA()->isRealized());
    add_category_to_loadable_list(cat);
    }
    }

    而当从此进入

    realizeClassWithoutSwift
    方法,进而调用
    methodizeClass
    方法时,此时
    methodizeClass
    方法中
    cats
    不为空,然后调用
    attachCategories()
    方法,将分类的方法贴到主类里面。

    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/);

    总结

      本篇介绍了 懒加载类非懒加载类 的加载,以及 懒加载分类非懒加载分类 搭配 懒加载类非懒加载类 的4种组合情况的加载原理。

    • 实现了

      load
      方法的类为
      非懒加载类
      ,在启动时初始化

    • 未实现

      load
      方法的方法的类为
      懒加载类
      ,在调用的时候初始化

    • 非懒加载类
      的加载:

      read_iamges
    • 循环读取非懒加载类,将其加到内存,
      addClassTableEntry(cls)
    • realizeClassWithoutSwift(cls)
      中,对
      cls
      supClass
      isa
      以及
      rw->ro
      等进行赋值,然后进入
      methodizeClass(cls)
      ,用
      ro
      中的数据对
      rw
      进行赋值。
  • 懒加载类
    的加载:在调用的时候初始化,比然进入
    lookUpImpOrForward
    方法

      进入方法查找流程,进入
      lookUpImpOrForward
    • !cls->isRealized()
      判断是否是
      懒加载类
      ,不是,开始方法查找
    • 懒加载类
      ,进入
      realizeClassMaybeSwiftAndLeaveLocked
    • 调用
      realizeClassWithoutSwift
  • 懒加载分类
    的加载是在编译期处理的,直接添加到相应的
    data()
    ro
    中,在类初始化的时候,直接用
    ro->baseMethods()
    rw->methods
    赋值。

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

      read_image
      中 对分类的处理
    • 判断是否是
      对象方法
      还是
      类方法
    • 为类添加未附加的类别,
      addUnattachedCategoryForClass
    • 判断是否是
      懒加载类
      cls->isRealized()
      ,此次为
      懒加载类
      ,不进入判断
    • 进入
      prepare_load_methods
    • 进入
      realizeClassWithoutSwift
    • 进入
      methodizeClass
      ,此时
      category_list *cats
      不为空
    • 调用
      attachCategories()
      方法,将分类的方法贴到主类里面。
  • 非懒加载分类
    的加载 +
    非懒加载类

      read_image
      中 对分类的处理
    • 判断是否是
      对象方法
      还是
      类方法
    • 为类添加未附加的类别,
      addUnattachedCategoryForClass
    • 判断是否是
      懒加载类
      cls->isRealized()
      ,此次为
      非懒加载类
      ,进入判断
    • 进入
      remethodizeClass
    • 调用
      attachCategories()
      方法,将分类的方法贴到主类里面。
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    亮亮不想说话 发布了18 篇原创文章 · 获赞 10 · 访问量 269 私信 关注
  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: