类&分类的加载

2020-03-23  本文已影响0人  85ca4232089b

类的加载:

  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
上一篇下一篇

猜你喜欢

热点阅读