入口
之前已经了解到
map_images是dyld在registerObjCNotifiers,中调用的,具体的实现还在libobjc源码中, 在objc_init中注册到dyld,_dyld_objc_notify_register(&map_images, load_images, unmap_image);
分析
map_image
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
// 互斥锁
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
- 参数分析
count=objcImageCount,image的Countpaths[]=imageFilePath,文件路径mhdrs[]=imageLoadAddress, 地址
map_images_nolock
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
// 主要变量
// 第一次
static bool firstTime = YES;
// hList 数组
header_info *hList[mhCount];
// header 个数
uint32_t hCount;
//SEL count
size_t selrefCount = 0;
// 主要逻辑
if (firstTime) {
// 处理共享缓存
preopt_init();
}
// mhdr 地址
// mhPaths 文件路劲
// 添加到 header_info 单项链表中,FirstHeader LastHeader
uint32_t i = mhCount;
while (i--) {
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
// 将header_info 添加到 hList中
hList[hCount++] = hi;
}
if (firstTime) {
// 初始化并注册内部的 namedSelectors 表
sel_init(selrefCount);
// AutoreleasePoolPage 初始化, 是双向链表,自动释放池
// SideTableMap 初始化 -> weak_table_t弱引用表 RefcountMap 引用表
// AssociationsManager 初始化, 存储的是锁,锁的散列表, 也就是关联列表
arr_init();
if (hCount > 0) {
//重要,读取images, hList:header_info *[header_info *], 装的是 header_info 的指针
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
// 调用镜像中的所有 load方法
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
- 第一次,处理共享缓存
- 遍历循环创建
header_info链表,并将header_info添加到hList中 - 调用
_read_images - 调用镜像文件中所有
load方法
read_images
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
// 主要逻辑
// 只走一次
if (!doneOnce) {
doneOnce = YES;
// 初始化 taggedPointer 混淆器
initializeTaggedPointerObfuscator();
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 创建 NXMapTable
// NXStrValueMapPrototype = _mapStrHash(字符串hash),字符串是否相等(_mapStrIsEqual)
// 除共享缓存之外的所有类
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
//修复预编译阶段的 @selector 引用
// 遍历hList
for (EACH_HEADER) {
// MachO 只读区 __DATA_CONST __objc_selrefs section中获取 sels
SEL *sels = _getObjc2SelectorRefs(hi, &count);
// 遍历所有的 sels
(i = 0; i < count; i++) {
// 将 SEL 转化为 const char * 类型, sel的指针
const char *name = sel_cname(sels[i]);
// dyld 查看name指向的有没有SEL, 有的话直接返回 dyld查找到的SEL
// 没有的话,将name copy一份 插入到 nameSelctors中
SEL sel = sel_registerNameNoLock(name, isBundle);
// 当注册过后的 sel 的地址 与 当前的地址不同时
if (sels[i] != sel) {
sels[i] = sel
}
}
}
ts.log("IMAGE TIMES: fix up selector references");
// 发现为未知的类,容错处理,一般不会走
for (EACH_HEADER) {
// 1. dyld共享缓存不存在或者过期
// 2. 模拟器运行
// 3. 覆盖了共享缓存
// 4, image 缺少 weak superclasses
// 5, 有未知的类
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
// mach-o 获取类
classref_t const *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
// 读取类
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized)
// 类有改动,但没有移除
if (newCls != cls && newCls) {
}
}
}
ts.log("IMAGE TIMES: discover classes");
ts.log("IMAGE TIMES: remap classes");
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
ts.log("IMAGE TIMES: discover protocols");
// mach-o : __objc_protolist section
NXMapTable *protocol_map = protocols();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for protolist {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
ts.log("IMAGE TIMES: fix up @protocol references");
for (EACH_HEADER) {
if (launchTime && hi->isPreoptimized())
continue;
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
// 如果已经 load_image, 就加载分类
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
ts.log("IMAGE TIMES: discover categories");
// 实现 非懒加载的类(类里实现的了 load方法的和 有静态初始化方法的)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 将类和元类加到 allocatedClasses 中
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
}
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
ts.log("IMAGE TIMES: realize future classes");
}
- 可以通过环境变量
OBJC_PRINT_IMAGE_TIMES = YES打印 EACH_HEADER遍历hList- 第一次,初始化 taggedPointer 混淆器,然后三分之四负载因子开辟内存,创建
gdb_objc_realized_classes,gdb_objc_realized_classes用词不当,实际是不在共享缓存的所有类,不管有没有实现
ts.log("IMAGE TIMES: fix up selector references");- 修复 mach-o
@selector,遍历hList - 从
Mach-O中取出sels, 注册sel,插入到nameSelctiors - 当前
sel地址不等于注册后的sel地址,则修正
- 修复 mach-o
ts.log("IMAGE TIMES: discover classes");- 查找类,未知类的话,就从
mach-o中重新读取,加载到内存中 - 遍历读取类,
addNamedClass将类 插入到gdb_objc_realized_classes中 addClassTableEntry(cls), 将类和元类插入到allocatedClasses中
- 查找类,未知类的话,就从
ts.log("IMAGE TIMES: remap classes");- 在
remapped_class_map查找 - 重新映射没有被镜像文件加载进来的类
- 一般不会走
- 在
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");- 修复老的 消息发送
- 真机不会走
ts.log("IMAGE TIMES: discover protocols");- 读取mach-o中的协议到内存中
- 从
mach-o中读取,protocollist readProtocol, 创建protocol_t插入到protocol_map中
ts.log("IMAGE TIMES: fix up @protocol references");- 修复 mach-o 中协议的指针地址
remapProtocolRef重新映射内存中的协议,不相同则重新赋值
ts.log("IMAGE TIMES: discover categories");didInitialAttachCategories默认是false- 加载分类,前提是image已经走了
load_image
ts.log("IMAGE TIMES: realize non-lazy classes");- 实现非懒加载的类
- 当类实现
+load的方法,就是非懒加载方法 - 所以
+load会导致类提前实现 addClassTableEntry, 将类和元类加到allocatedClasses中realizeClassWithoutSwift实现类,重点,也就是ro, rw, 类别的处理
ts.log("IMAGE TIMES: realize future classes");- 如果有未处理的类就实现新的解析未知类,以防止CF修改他们
- 一般不会走
总结
map_images在dyld注册是调用- 主要是将
mach-o映射到内存中 - 修复了
mach-o中sel与内存中nameSelctiors映射 - 将
mach-o中protocol读取到内存protocol_map中 - 如果类实现了
load方法, 就会实现类,非懒加载