美文网首页
类&分类的加载

类&分类的加载

作者: 85ca4232089b | 来源:发表于2020-03-23 11:24 被阅读0次

    类的加载:

    1. libObjc 向 dyld 注册了回调 _dyld_objc_notify_register,当 dyld 把 App 以及 App 所依赖的一系列 Mach-O 镜像加载到当前 App 被分配的内存空间之后,dyld 会通过 _dyld_objc_notify_mapped 也就是 map_images 来通知 libObjc 来完成具体的加载工作,map_images 被调用之后会来到 _read_images
    2. 插入所有的类 到 gdb_objc_realized_classes 哈希表中(插入方式为 类名为 key,类对象为value, 不包括通过 共享缓存 里面的类),同时还会把类插入到 allocatedClasses 这个集合里面,注意,allocatedClasses 的类型为 NXHashTable,可以类比为 NSSet,而 gdb_objc_realized_classes 的类型为 NXMapTable,可以类比为 NSDictionary
    3. 将所有的 Protocol 插入到 readProtocol 哈希表中(插入方式为:Protocol 名称为 key,Protocol 为 value)
    4. 将所有的 SEL 插入到 namedSelectors 哈希表中(插入方式为:SEL 名称为 key,SEL 为value)

    • 懒加载类
    在第一次发送某个消息的时候,是没有缓存的,所以会来到一个非常重要的方法叫 lookUpImpOrForward

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

    if (slowpath(!cls->isRealized())) {
    slowpath小概率事件:意思是cls有很小的概率是没有实现的
    • 类第一次发送消息的时候是没有缓存的,所以会来到 _class_lookupMethodAndLoadCache3
    • _class_lookupMethodAndLoadCache3 会调用 lookUpImpOrForward
    • lookUpImpOrForward 内部会进行一下判断,如果 cls 没有被实现,会调用 realizeClassMaybeSwiftAndLeaveLocked 方法
    • realizeClassMaybeSwiftAndLeaveLocked 方法又会调用 realizeClassMaybeSwiftMaybeRelock 方法
    • realizeClassMaybeSwiftMaybeRelock 方法内部会进行一下是否是 Swift 的判断,如果不是 Swift 环境的话,就会来到 realizeClassWithoutSwift ,也就是最终的类的加载的地方

    • 非懒加载类

        // Realize non-lazy classes (for +load methods and static instances)实现非懒加载类(实现了 +load 方法和静态实例)
        for (EACH_HEADER) {
            classref_t const *classlist = 
                _getObjc2NonlazyClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                if (!cls) continue;
                printf("_getObjc2NonlazyClassList Class:%s\n",cls->mangledName());
                
                addClassTableEntry(cls);
    
                if (cls->isSwiftStable()) {
                    if (cls->swiftMetadataInitializer()) {
                        _objc_fatal("Swift class %s with a metadata initializer "
                                    "is not allowed to be non-lazy",
                                    cls->nameForLogging());
                    }
                }
                realizeClassWithoutSwift(cls, nil);
            }
        }
    

    分类的加载

    • 懒加载分类
    分类直接在编译时加载到了类的 ro 里面,然后在运行时被拷贝到了类的 rw 里面.
    分类的加载其实跟类的懒加载与否并没有关系,也就是说懒加载的分类都是在编译时期被加载的。

    • 非懒加载分类&懒加载类
    走的不是发送消息的流程,而走的是 load_images 里面的 prepare_load_methods 方法

    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, i;
    
        runtimeLock.assertLocked();
    
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            schedule_class_load(remapClass(classlist[i]));
        }
    
        category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = categorylist[i];
            Class cls = remapClass(cat->cls);
            if (!cls) continue;  // category for ignored weak-linked class
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class extensions and categories on Swift "
                            "classes are not allowed to have +load methods");
            }
            realizeClassWithoutSwift(cls, nil);
            ASSERT(cls->ISA()->isRealized());
            add_category_to_loadable_list(cat);
        }
    }
    

    调用了 realizeClassWithoutSwift 方法来加载类的。而上面的 _getObjc2NonlazyCategoryList 方法显示就是获取的所有的非懒加载分类,然后遍历这些非懒加载分类,然后去加载这些分类所依赖的类。非懒加载分类让我们的懒加载类实现提前了,懒加载类并不一定只会在第一次消息发送的时候加载,还要取决于有没有非懒加载的分类,如果有非懒加载的分类,那么就走的是 load_images 里面的 prepare_load_methods 的 realizeClassWithoutSwift 。
    • 非懒加载分类&非懒加载类
    attachCategories 这个方法只会在实现了 load 方法的分类下才会被调用,而来到 attachCategories 之前又取决于类是否为懒加载,如果是懒加载,那么就在 load_images 里面去处理,如果是非懒加载,那么就在 map_images 里面去处理。

    总结

    • 没有实现 load 方法的分类由编译时确定
    • 实现了 load 方法的分类由运行时去确定

    • 懒加载分类 + 懒加载类
     类的加载在第一次消息发送的时候,而分类的加载则在编译时load_images
    • 懒加载分类 + 非懒加载类
      类的加载在 _read_images 处,分类的加载还是在编译时load_images
    • 非懒加载分类 + 懒加载类
      类的加载在 load_images 内部,分类的加载在类加载之后的 methodizeClass
    • 非懒加载分类 + 非懒加载类
      类的加载在 _read_images 处,分类的加载在类加载之后的 reMethodizeClass
    
    消息查找流程.jpg

    相关文章

      网友评论

          本文标题:类&分类的加载

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