我们初步探索了dyld和objc的关联
,也因此引出将类加载到内存
中,最关键的就是两个函数map_images
和load_images
map_images
:主要是管理文件中和动态库中的所有符号,即class、protocol、selector、category等load_images
:加载执行load方法
静态库、动态库以及自己写的代码通过编译会生成mach-o形式的可执行文件
,再从Mach-O
中读取到内存。
map_images:加载镜像文件到内存
map_images
方法的主要作用就是将Mach-O可执行文件
中的类信息
加载到内存
- 进入
map_images
的源码
- 进入
map_images_nolock
源码,其关键代码是_read_images
_read_images 源码实现
_read_images
主要是加载类信息(即类、分类、协议等)
到内存
,进入_read_images源码实现,主要分为以下几部分:
- 条件控制进行的一次加载
- 修复预编译阶段的@selector的混乱问题
- 错误混乱的类处理
- 修复重映射一些没有被镜像文件加载进来的类
- 修复一些消息
- 当类里面有协议时:readProtocol 读取协议
- 修复没有被加载的协议
- 分类处理
- 类的加载处理
- 没有被处理的类,优化那些被侵犯的类
1、条件控制进行的一次加载
在doneOnce流程中通过NXCreateMapTable
创建表,即创建一张类的哈希表
gdb_objc_realized_classes
,目的是为了方便快捷地查找类
查看gdb_objc_realized_classes
的注释说明,这张哈希表用于存储不在dyld共享缓存中且已命名的类
,无论类是否实现
。
2.修复预编译阶段的@selector的混乱问题
主要是通过_getObjc2SelectorRefs拿到Mach_O中的静态段__objc_selrefs,遍历列表调用sel_registerNameNoLock将SEL添加到namedSelectors哈希表中
3、错误混乱的类处理
主要是从Mach-O中取出所有类
,在遍历进行处理。当某些类已经被移动了,但未被删除的时候,就会在这里进行处理
- 通过断点调试,在未执行
readClass
方法前,cls只是一个地址
- 在执行完
readClass
方法之后,newCls是一个类的名称
所以到了这一步,类的信息
目前仅仅存储了地址+名称
4、修复重映射一些没有被镜像文件加载进来的类
主要是将未映射的Class 和Super Class进行重映射,其中
- _getObjc2ClassRefs是获取Mach-O中的静态段__objc_classrefs即类的引用
- _getObjc2SuperRefs是获取Mach-O中的静态段__objc_superrefs即父类的引用
- 通过注释可以得知,被remapClassRef的类都是懒加载的类,所以最初经过调试时,这部分代码是没有执行的
5、修复一些消息
主要是通过_getObjc2MessageRefs
获取Mach-O的静态段
__objc_msgrefs`,并遍历通过fixupMessageRef将函数指针进行注册,并fix为新的函数指针
6、当类里面有协议时:readProtocol 读取协议
7、修复没有被加载的协议
8、分类处理
分类的加载,会在后面的文章中介绍…
9、类的加载处理
主要是实现类的加载处理
,实现非懒加载类
- 通过
_getObjc2NonlazyClassList
获取Mach-O的静态段__objc_nlclslist
非懒加载类表 - 通过
addClassTableEntry
将非懒加载类插入类表,存储到内存,如果已经添加就不会载添加,需要确保整个结构都被添加 - 通过
realizeClassWithoutSwift
实现当前的类
,因为前面3中的readClass读取到内存的仅仅只有地址+名称,类的data数据并没有加载出来
10、没有被处理的类,优化那些被侵犯的类
主要是实现没有被处理的类,优化被侵犯的类
接下来 我们需要重点关注一下readClass以及realizeClassWithoutSwift这两个方法。
readClass:读取类
readClass
主要是去读取类
,在未执行readClass之前,cls只是一个地址,在执行完readClass之后,返回的newCls已经有了名称
。
- 为了让自己定义的LGPerson类进来,我们开readClass源码里面写了一些判断条件
// 如果想自定义的类进入,自己加一个判断
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
auto kc_ro = (const class_ro_t *)cls->data();
printf("%s -- 研究重点--%s\n", __func__,mangledName);
}
复制代码
- readClass源码实现如下,关键代码是addNamedClass和addClassTableEntry