07--应用加载04--分类以及(非)懒加载分析[load_images]

TOC

load_images 流程分析

load_images 主要分为两个流程:

  • prepare_load_methods:准备 load 方法
  • call_load_methods:调用 load 方法

流程一:prepare_load_methods

处理非懒加载类

  1. 获取所有的非懒加载类(实现了load方法的类)

    classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
    
  2. 计划添加(load)类

    这个方法中有一个递归,递归父类

    保证父类的load方法在子类的load方法之前执行

    static void schedule_class_load(Class cls)
    {
        // Ensure superclass-first ordering
        schedule_class_load(cls->superclass);
    
        add_class_to_loadable_list(cls);
    }
    
  3. loadable_classes 表分析:这是一个全局数组

    • 初始化以及扩容
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    • 添加实现load方法的类的操作
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
    
    • 全局变量的定义
    static struct loadable_class *loadable_classes = nil;
    static int loadable_classes_used = 0;
    static int loadable_classes_allocated = 0;
    
     - loadable_class:实现load方法的类的数组
     - loadable_classes_used:当前记录的类的个数
     - loadable_classes_allocated:当前创建的类的个数
    
    • loadable_class 的结构:cls + method
    struct loadable_class {
        Class cls;  // may be nil
        IMP method;
    };
    

处理非懒加载类

相同之处

  1. 获取所有的非懒加载分类:category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
  2. 添加分类到 loadable_list 中:add_category_to_loadable_list(cat);,这个方法的实现流程和逻辑与 add_class_to_loadable_list(cls); 方法一模一样。

不同之处

  1. 添加分类前会调用一次类的初始化方法:realizeClassWithoutSwift(cls, nil);

  2. 分类的信息是保存在 loadable_categories 变量中;

  3. 分类记录的信息是:分类(cate)+方法(method)

    struct loadable_category {
        Category cat;  // may be nil
        IMP method;
    };
    

流程二:call_load_methods

完整的分析了上面的流程,再来看这个流程就简直不要太简单。为什么这么说呢?

源码很短,直接上(只保留主要代码):

void call_load_methods(void)
{
    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);
}

苹果给出的注释已经非常详细了

  1. 调用类的load方法:call_class_loads();,就是这个 loadable_classes 数组中的类的load方法;
  2. 调用分类的load方法:call_category_loads();,就是 loadable_categories 数组中的分类的load方法;
  3. 遍历下去,直到全部调用

这里解释了2个问题,看懂这个流程,简直太简单了,我都不想解释了。

1. 主类的load方法和分类的load方法是否会同时调用还是只调用某一个

会同时调用

2. 为什么主类的load方法在分类的load方法前面调用

因为在 call_load_methods 方法中,先调用的是 call_class_loads(); 主类的load方法,然后再调用 call_category_loads(); 分类的laod方法

分类的初探

分类的结构:通过cpp文件分析

_category_t
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;
};

分类的结构:通过objc源码分析

image

在 objc 源码里面也有很清晰的判断 是否是元类

  • instance_methods:attachList 到类里面
  • class_methods: attachList 到元类里面

为什么分类的方法会“覆盖”主类的方法

  1. 分类是在类的初始化完成之后才加载的;
  2. 分类的方法是通过attach到类的方法列表中;
  3. 根据attachLists的流程,后添加的方法被插在了列表前面;
  4. 在寻找方法的时候,会按照列表的顺序查找,如果找到了指定方法就不会再往后面寻找,所以造成了“覆盖”的假象,实际上两个方法都在方法列表中。

懒加载类与非懒加载类

懒加载类

  • 没有实现load方法
  • 在运行时调用这个类的时候才会加载

非懒加载类

  • 实现了load方法
  • 如果有子类实现了load方法,则这个类也在编译阶段加载
  • 如果在其他类的load方法中被调用,则这个类也在编译阶段加载
  • 将类的加载提前到了编译阶段

怎么理解将类的加载提前到了编译阶段?

我们需要先知道编译阶段做了啥。

在平时开发中,cmd+b,也就是我们常说的 编译一下,但真的只是 编译一下 吗?build 是构建的意思,既然是构建,肯定要有构建的东西出来。确实是的,构建的结果就是藏在我们xcode的项目目录的 product 文件夹下面的那个文件,俗称 APP,但实际上是一个可执行文件。所以,编译可以理解为从代码到可执行文件的过程

这个过程经历了啥呢。前面将 [_dyld] 的时候讲过了,流程是:

源文件(.m/.h)
-> 预处理(.cpp)
-> 预处理完成(.i)
-> Compile编译(gcc)
-> Assembly(.s)
-> Assembly(as)
-> 镜像文件(.o)
-> _dyld链接
-> 可执行文件

类与分类的搭配加载

1. 懒加载的分类(没有实现load)

1. 懒加载类(没有实现load)

  1. _objc_init

  2. _read_images

  3. _getObjc2CategoryList:取得所有懒加载分类

    GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");
    GETSECT(_getObjc2NonlazyCategoryList, category_t *,    "__objc_nlcatlist");
    
  4. addUnattachedCategoryForClass:保存分类的方法到表中

    category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);
    NXMapTable *cats = unattachedCategories();
    NXMapInsert(cats, cls, list);
    
  5. load_images

  6. prepare_load_methods

  7. realizeClassWithoutSwift

  8. methodizeClass

  9. attachCategories

2. 非懒加载类(实现了load)

  1. _objc_init
  2. _read_images
  3. _getObjc2CategoryList
  4. addUnattachedCategoryForClass
  5. remethodizeClass
  6. tachCategories

2. 非懒加载分类(实现了load)

1. 懒加载类(没有实现load)

image
  1. alloc
  2. _objc_msgSend_uncached
  3. lookUpImpOrForward
  4. initializeAndLeaveLocked
  5. realizeClassWithoutSwift:在这里将所有方法存在了ro中
  6. methodizeClass

2. 非懒加载类(实现了load)

image
  1. _objc_init
  2. _read_images
  3. realizeClassWithoutSwift
  4. methodizeClass
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348