iOS 类的加载(非懒加载类)
2020-01-28 本文已影响0人
Joker_King
在我们日常的开发中我们最常见的就是类,那么我们我们声明的类是如何被系统加载进来的呢?接下来我们就围绕着这个话题进行探索。
我们的dyld在初始化主程序时来到_objc_init
函数中并注册了相应的回调函数。
void _objc_init(void)
{
//设置系统的环境变量。
environ_init();
//线程相关的处理。
tls_init();
//运行C ++静态构造函数。
static_init();
lock_init();
//注册异常的回调。
exception_init();
//调用dyld的函数注册一个回调,并执行回调函数。
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
map_images
处理由dyld映射的镜像文件。
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);
}
1、 _read_images初探
把镜像文件中的数据读取到内存中
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
//.......保留主要逻辑的代码
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
//.......保留主要逻辑的代码
}
1.1第一次进来 - 开始创建表
当我们的应用通过冷启动的方式启动时会执行这个流程
if (!doneOnce) {
doneOnce = YES;
//省略。。。
//执行taggedPointers相关处理
if (DisableTaggedPointers) {
disableTaggedPointers();
}
initializeTaggedPointerObfuscator();
//省略。。。
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
//创建两张表
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
//只要不是共享缓存里面的类,无论是否实现,都会被存在这里。
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
//存储所有被开辟过的类,无论是元类还是类。
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
这段代码的主要作用就是,为我们创建两张表来存储类。
-
gdb_objc_realized_classes
只要不是共享缓存里面的类,无论是否实现,都会被存在这里。 -
allocatedClasses
存储所有被开辟过的类,无论是元类还是类。
gdb_objc_realized_classes
表中的类可能会包含allocatedClasses
表中的类。
1.2类的实现
for (EACH_HEADER) {
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
//省略。。。
//将已开辟过的类添加进allocatedClasses表中
addClassTableEntry(cls);
// 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
1.2.1 realizeClassWithoutSwift
- 从
cls
中取出ro
,并给rw
开辟内存空间。
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// 省略非正常流程的处理。
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
//将ro赋值给rw中的ro
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
//将rw设置进cls
cls->setData(rw);
}
- 递归处理父类和元类。
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
递归的结束条件在开头的部分。
if (!cls) return nil;
- 父类和元类的归属关系。
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
- 将此类链接到父类的子类列表中。
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
- 将
ro
中的数据复制到rw
中。
// Attach categories
methodizeClass(cls);
1.2.2 methodizeClass
- 复制类的方法列表,协议列表,和属性列表到
rw
中。
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
- 将分类中的方法列表,属性列表添加到
rw
中。
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
1.2.3 attachLists
这是真正执行添加操作的地方。
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
添加逻辑分为以下几步
- 多个list多多个list
-- 计算总list的个数=已有list的个数+新增list的个数。
-- 执行扩容操作,重新开辟内存。
-- 将原来的list平移到数组的末尾。再将新增的list拷贝到数组的开头。(这一步也解释了为什么分类中的方法会先被查找到)。 - 0list对1list
-- 直接将新增的列表添加进去。 - 1list对多个list
-- 计算总list的个数=1+新增list的个数。
-- 执行扩容操作,重新开辟内存。
-- 将原来的list平移到数组的末尾。再将新增的list拷贝到数组的开头。(这一步也解释了为什么分类中的方法会先被查找到)。
memcpy函数:从源内存地址的起始位置开始拷贝若干个字节到新的目标内存地址中。
1.2.4 class_rw_t存储方法协议属性的方式
class_rw_t
是以二位数组的方式来存储的,大致的形式如下。
2、问题扩展。
attachLists在哪些地方会被调用?
- 类的加载-处理方法属性协议
methodizeClass
。 - 动态添加方法-
addMethods
。 - 动态添加属性-
_class_addProperty
。 - 动态添加协议-
class_addProtocol
。 - 分类的加载-
attachCategories
。