美文网首页
images加载二:懒加载和分类的加载

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

作者: 半边枫叶 | 来源:发表于2020-01-12 18:40 被阅读0次

    类的懒加载流程

    在我们的上篇文章中提到:_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加载二:懒加载和分类的加载

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