美文网首页
iOS 分类的加载

iOS 分类的加载

作者: Joker_King | 来源:发表于2020-01-30 11:38 被阅读0次

    在日常开发中我们想要为一个类(系统类或者自定义类)添加方法的时候,最常用的手段就是写一个分类,那么我们创建分类是何时被加载进类中的呢?

    通过objc的源码可知我们在从Mach-O中读取分类时分为两中。

    GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");
    GETSECT(_getObjc2NonlazyCategoryList, category_t *,    "__objc_nlcatlist");
    

    就是懒加载分类和非懒加载分类

    1、懒加载分类

    懒加载分类,也就是没有实现+(void)load方法的分类。

    懒加载类-懒加载分类
    非懒加载类-懒加载分类
    通过两次的断点调试,我们发现懒加载的分类,在运行时期间没有进行添加分类的操作,我们来看看分类中的方法是否被添加进来。

    1.1、lldb调试分类是否被添加

    • 获取类结构
    (lldb) x/4gx cls
    0x1000012c0: 0x001d800100b390f1 0x0000000100b390f0
    0x1000012d0: 0x00000001003dc230 0x0000000000000000
    
    • 通过地址偏移拿到bits
    (lldb) p (class_data_bits_t *)0x1000012f0
    (class_data_bits_t *) $1 = 0x00000001000012f0
    
    • 获取bits中的class_rw_t
    (lldb) p $1->data()
    (class_rw_t *) $3 = 0x0000000100b39140
    
    • 查看一下class_rw_t中的内容
    (lldb) p *$3
    (class_rw_t) $4 = {
      flags = 2148139008
      version = 7
      ro = 0x00000001000011f0
      methods = {
        list_array_tt<method_t, method_list_t> = {
           = {
            list = 0x0000000100001168
            arrayAndFlag = 4294971752
          }
        }
      }
      properties = {
        list_array_tt<property_t, property_list_t> = {
           = {
            list = 0x0000000000000000
            arrayAndFlag = 0
          }
        }
      }
      protocols = {
        list_array_tt<unsigned long, protocol_list_t> = {
           = {
            list = 0x0000000000000000
            arrayAndFlag = 0
          }
        }
      }
      firstSubclass = nil
      nextSiblingClass = 0x00007fff92d22080
      demangledName = 0x0000000000000000
    }
    
    • 接下来我们再看一下ro中的内容
    (lldb) p $4.ro
    (const class_ro_t *) $5 = 0x00000001000011f0
    (lldb) p *$5
    (const class_ro_t) $6 = {
      flags = 389
      instanceStart = 40
      instanceSize = 40
      reserved = 0
      ivarLayout = 0x0000000000000000
      name = 0x0000000100000f7b "LGTeacher"
      baseMethodList = 0x0000000100001168
      baseProtocols = 0x0000000000000000
      ivars = 0x0000000000000000
      weakIvarLayout = 0x0000000000000000
      baseProperties = 0x0000000000000000
      _swiftMetadataInitializer_NEVER_USE = {}
    }
    
    • 继续查看ro中的baseMethodList
    (lldb) p $6.baseMethodList
    (method_list_t *const) $7 = 0x0000000100001168
    (lldb) p *$7
    (method_list_t) $8 = {
      entsize_list_tt<method_t, method_list_t, 3> = {
        entsizeAndFlags = 26
        count = 3
        first = {
          name = "sayMaster"
          types = 0x0000000100000f8c "v16@0:8"
          imp = 0x0000000100000c40 (objc-debug`+[LGTeacher sayMaster] at LGTeacher.m:16)
        }
      }
    }
    
    (lldb) p $8.get(0)
    (method_t) $14 = {
      name = "sayMaster"
      types = 0x0000000100000f8c "v16@0:8"
      imp = 0x0000000100000c40 (objc-debug`+[LGTeacher sayMaster] at LGTeacher.m:16)
    }
    (lldb) p $8.get(1)
    (method_t) $15 = {
      name = "load"
      types = 0x0000000100000f8c "v16@0:8"
      imp = 0x0000000100000c10 (objc-debug`+[LGTeacher load] at LGTeacher.m:12)
    }
    (lldb) p $8.get(2)
    (method_t) $16 = {
      name = "cate_classMethod2"
      types = 0x0000000100000f8c "v16@0:8"
      imp = 0x0000000100000da0 (objc-debug`+[LGTeacher(test) cate_classMethod2] at LGTeacher+test.m:30)
    }
    

    通过上述的lldb调试,我们发现,我们分类中的方法已经被添加到ro中了。
    所以我们得出懒加载的分类- 编译处理 - 直接处理 data() - ro。

    2、非懒加载分类

    2.1、非懒加载类-非懒加载分类

    非懒加载类-非懒加载分类
    • read_images - realizeClassWithoutSwift - methodlizeClass - addUnattachedCategoryForClass(这里还没有)
    • 继续read_images流程
    // Discover categories. 
        for (EACH_HEADER) {
            category_t **catlist = 
                _getObjc2CategoryList(hi, &count);
            bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
            for (i = 0; i < count; i++) {
                category_t *cat = catlist[i];
                Class cls = remapClass(cat->cls);
                //省略。。。。
                //下面的代码实现了实例分别对实例方法和类方法做了区分
                bool classExists = NO;
                if (cat->instanceMethods ||  cat->protocols  
                    ||  cat->instanceProperties) 
                {
                    addUnattachedCategoryForClass(cat, cls, hi);
                    if (cls->isRealized()) {
                        remethodizeClass(cls);
                        classExists = YES;
                    }
                    if (PrintConnecting) {
                        _objc_inform("CLASS: found category -%s(%s) %s", 
                                     cls->nameForLogging(), cat->name, 
                                     classExists ? "on existing class" : "");
                    }
                }
    
                if (cat->classMethods  ||  cat->protocols  
                    ||  (hasClassProperties && cat->_classProperties)) 
                {
                    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                    if (cls->ISA()->isRealized()) {
                        remethodizeClass(cls->ISA());
                    }
                    if (PrintConnecting) {
                        _objc_inform("CLASS: found category +%s(%s)", 
                                     cls->nameForLogging(), cat->name);
                    }
                }
            }
        }
    

    上述代码分别对实例方法和类方法进行了区分,原因是实例方法存储在类对象中,而类方法是存储在元类中,所以要分别进行处理。

    if (cls->isRealized()) {
       remethodizeClass(cls); -> 实现类信息
    }
    

    最后通过attachCategories加载分类数据进来

    2.2、懒加载类-非懒加载分类

    懒加载类-非懒加载分类
    通过调用堆栈我们可以清楚的看到在load_image阶段因为需要调用+(void)load方法所以类的加载被提前了,也就是在prepare_load_methods函数中执行了类的加载,然后继续在将分类中的方法加载到类中。

    至此,我们关于分类的加载就分析完毕了。

    相关文章

      网友评论

          本文标题:iOS 分类的加载

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