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()中就已经存在懒加载的分类方法了。

你可能感兴趣的:(images加载二:懒加载和分类的加载)