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