本系列博客是本人的源码阅读笔记,如果有 iOS 开发者在看 runtime 的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论
本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime
概述
今天我们开始讲解 map_images_nolock
中调用的最后一个方法:_read_images
,其调用路径为:
- void _objc_init(void)
- void _dyld_objc_notify_register();
- void map_2_images();
- void map_images_nolock();
- void _read_images();
这个方法作用就是读取各个 section 中的数据并放到缓存中,这里的缓存大部分都是全局静态变量,载体就是我们之前分析的 hashmap,我们先回顾上一篇文章中的所有 hashmap:
pendingInitializeMap 、 category_map 、nonmeta_class_map 、future_named_class_map 、remapped_class_map 、protocol_map 、gdb_objc_realized_classes。
我们把这里面的 hashmap 分为几类:
-
关于 class 的:
gdb_objc_realized_classes、remapped_class_map、future_named_class_map、nonmeta_class_map -
关于 protocol 的:
protocol_map -
关于 category 的:
category_map
本文带大家了解 _read_images 方法的过程中会揭开其中的几个 hashmap 的作用。
_read_images 源代码如下(过滤一些,只留下主要代码)
if (!doneOnce) {
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
for (EACH_HEADER) {
classref_t *classlist = _getObjc2ClassList(hi, &count);
}
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
}
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
}
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
}
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
}
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
}
for (EACH_HEADER) {
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
}
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
}
发现很多类似的方法,这些方法在笔者之前的文章中已经讲解过,都是获取 section 的方法:
// function name content type section name
GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs");
GETSECT(_getObjc2ClassRefs, Class, "__objc_classrefs");
GETSECT(_getObjc2SuperRefs, Class, "__objc_superrefs");
GETSECT(_getObjc2ClassList, classref_t, "__objc_classlist");
GETSECT(_getObjc2NonlazyClassList, classref_t, "__objc_nlclslist");
GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist");
GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList, protocol_t *, "__objc_protolist");
GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
总结如下:
section名 | 调用的方法 | 返回类型 |
---|---|---|
__objc_classlist | _getObjc2ClassList | classref_t |
__objc_selrefs | _getObjc2SelectorRefs | SEL |
__objc_msgrefs | _getObjc2MessageRefs | message_ref_t |
__objc_protolist | _getObjc2ProtocolList | protocol_t |
__objc_protorefs | _getObjc2ProtocolRefs | protocol_t |
__objc_nlclslist | _getObjc2NonlazyClassList | classref_t |
__objc_catlist | _getObjc2CategoryList | category_t |
因此 _read_images 方法的作用也很明显了: 把对应 section 里的数据取出来,然后进行加工,最后添加到缓存中。虽然说 _read_images 只有几百行,但其中又会调用其他很多方法,因此总的来说 _read_images 方法还是比较复杂的。这里只摘取一些 section 数据获取后的处理流程。
__objc_classlist
之前已经大概说过 __objc_classlist
这个 section 表示的是项目中全部类列表,与之关联的一个 section 为: __objc_classrefs
,表示项目中被引用的类列表。列一张表看一下:
区 | __objc_classlist | __objc_classrefs |
---|---|---|
含义 | 项目中全部类列表 | 项目中被引用的类列表 |
对应方法 | _getObjc2ClassList | _getObjc2ClassRefs |
对应全局变量 | gdb_objc_realized_classes | noClassesRemapped |
这两个 section 的数据都在 _read_images 方法中,这里做个大概讲解:
- 创建:
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
- 插入:
- Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
- addNamedClass(cls, mangledName, replacing);
- NXMapInsert(gdb_objc_realized_classes, name, cls);
__objc_selrefs
区 | __objc_selrefs |
---|---|
含义 | 方法列表 |
方法名 | _getObjc2SelectorRefs |
对应全局变量 | namedSelectors |
- 创建
namedSelectors = NXCreateMapTable(NXStrValueMapPrototype,
(unsigned)SelrefCount);
- 插入
NXMapInsert(namedSelectors, sel_getName(result), result);
- 获取
result = (SEL)NXMapGet(namedSelectors, name);
读者可以自行找到这几段代码的位置。
文章完整版请见笔者小专栏:https://xiaozhuanlan.com/runtime
总结
本文讲解了 _read_images 函数的作用是获取各个 section 中的数据并在此基础上做一些处理,希望对大家有所帮助。最后用一幅图总结一下本文涉及到的 section:
section
网友评论