images加载二:懒加载和分类的加载

类的懒加载流程

在我们的上篇文章中提到:_read_images方法中获取到classlist,然后循环使用realizeClassWithoutSwift对class进行了初始化处理。我们就在这个循环中打印所有的class,然后有个神奇的发现:如果我们自定义的类中没有实现+(void)load方法,那么上面的classlist中就不会存在这个类。只要实现了load方法的class才会存在。为什么会这样呢?
原来我们的系统对加载class进行了懒加载的优化,也就是如果没有用到某个class的话,就不加载,只有使用到的时候才会加载。因为load方法在编译的时候调用,所以也必须在此之前初始化这个class。所以如果实现了load方法的话,就会取消该class的懒加载优化,直接进行加载。
也就是说如果class实现了load方法,那么就以非懒加载方式加载,否则以懒加载方式加载。
Images加载一中研究的都是class的非懒加载,下面我们来研究下class懒加载的流程。

LGPerson *object = [LGPerson alloc];

我们再调用以上的代码,就会给LGPerson类对象发送消息。发送消息就会走我们熟悉的lookupImpOrForward方法。我们进入该方法,发现一下判断代码

    if (!cls->isRealized()) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

如果cls没有实现的话,就会走realizeClassMaybeSwiftAndLeaveLocked

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

然后调用realizeClassMaybeSwiftMaybeRelock

static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();

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

如果是OC方法的话,就会调用realizeClassWithoutSwift这个方法。这个方法不就是我们在研究Images加载一中,以非懒加载方式加载class时在_read_images调用的方法吗。
realizeClassWithoutSwift中完成了对class的ro、rw的赋值,以及递归父类和元类的初始化赋值。到此为止class的懒加载流程和非懒加载流程都调用了realizeClassWithoutSwift方法。
也就是说懒加载class是在第一次接受消息的时候进行加载的。

下面我们来看下分类的加载流程

首先我们添加几个分类方法,然后通过clang来看下分类的底层实现。通过clang命令生成对应的c++文件,c++文件内容比较多,我们可以直接拉到文件的底部查看我们的oc代码对应的内容。
很明显可以找到_category_t这个对象。然后我们可以搜索到_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的源码中搜索category_t找到在c语言中对应的定义。
结构体中包含了名字、对应的class以及协议、属性、实例方法列表和类方法列表。为什么这里的实例方法和类方法分成了两个列表呢?因为实例方法要attach到类的方法列表中,而类方法要attach到元类的方法列表中。

我们知道了类的加载分为懒加载和非懒加载,同样分类的加载也分为懒加载和非懒加载。这样就是有四个组合:

  • 懒加载类+非懒加载分类;
  • 非懒加载类+非懒加载分类;
  • 非懒加载类+懒加载分类;
  • 懒加载类+懒加载分类;
    好了,下面我们来一一的研究。
1、懒加载类+非懒加载分类;

因为分类为非懒加载,所以肯定会调用_read_images方法,然后走到该方法的处理分类的地方:

category_t **catlist = _getObjc2CategoryList(hi, &count);

然后一个for循环处理catlist:

for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            // ......省略一些无用的代码
            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" : "");
                }
            }
            // ......省略一些无用的代码
}

首先获取到list中的一个cat,然后调用了addUnattachedCategoryForClass方法,将category暂时存起来,此时class还没有初始化。
还记得_objc_init中的这个方法吗

_dyld_objc_notify_register(&map_images, load_images, unmap_image);

其中的load_images函数调用了prepare_load_methods函数,而在prepare_load_methods函数中获取到非懒加载的categoryList,如果存在非懒加载categoryList的话,就会在此时调用realizeClassWithoutSwift(cls);方法来初始化该class,然后开始调用methodizeClass方法,在methodizeClass方法中就会开始绑定categoryList到对应的class

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

这样本来以来,本来懒加载类是会在运行时接受消息的时候才会出初始化的,结果因为非懒加载的分类而被提前初始化了。

2、非懒加载类+非懒加载分类;

因为class为非懒加载,所以首先会加载初始化class;然后因为分类也是非懒加载的,所以会调用_read_images中的分类相关的加载初始化,在此过程中调用了下面绑定分类到class的方法:

addUnattachedCategoryForClass

这样在_read_images过程中完成了分类的attach。

3、非懒加载类+懒加载分类;

因为class为非懒加载,所以会直接会走正常的class的加载初始化流程:read_images---->realizeClassWithoutSwift---->methodlizeClass
初始化完成后,class的ro中就已经存在懒加载的分类方法了。这里编译器已经自动的将category方法加进去了;

4、懒加载类+懒加载分类;

这个流程和上面的3流程相似,就是入口不是read_images,而是lookupImpOrForward方法,
lookupImpOrForward--->realizeClassMaybeSwiftAndLeaveLocked--->realizeClassMaybeSwiftMaybeRelock--->realizeClassWithoutSwift---->methodlizeClass
然后class的data()中就已经存在懒加载的分类方法了。

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

推荐阅读更多精彩内容