1 前言
我们从iOS底层原理之—dyld与objc的关联中知道dyld关联objc
时,通过_read_images
函数中的readClass
函数来读取类的信息并将类关联起来,我们本文主要来探讨类的加载。
2 先看_read_images
函数
由于我们探究的是类的加载,且在iOS底层原理之—dyld与objc的关联中我们已经主要分析了下_read_images
函数的作用,因此我们主要看_read_images
函数中有关的部分代码,上代码
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
ts.log("IMAGE TIMES: discover classes");
2.1 分析调用readClass
部分代码
2.1.1 没有调用readClass
之前cls分析
在没有调用readClass
时,我们读取cls信息从上图中我们发现此时cls只是一串地址
2.1.2 调用readClass
之后cls分析
调用readClass
之后,我们读取cls信息从上图中我们得知cls已经由一串地址变成了LGPerson类。因此我们得出一定是readClsss
函数做了什么,我们来分析下readClsss
函数。
3 readClsss
函数分析
3.1 源码
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
auto kc_ro = (const class_ro_t *)cls->data();
printf("readClass: 这个是我要研究的 %s \n",LGPersonName);
}
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
源码中为了更好的区别出LGPerson
,特意添加了判断LGPerson
类型的代码
3.2 代码跟踪
通过代码调试,最终执行了addNamedClsss
函数在执行addNamedClsss
函数之前打印cls依然是一串地址。当执行完addNamedClsss
函数时,打印cls发现指向了LGPerson
因此我们得出addNamedClsss
对cls进行了指向操作。
3.3 addNamedClsss
函数
4 懒加载类与非懒加载类
4.1 定义
懒加载类
:推迟到第一次消息发送的时候才加载的类称为懒加载类
;非懒加载类
:在map_images
函数执行_read_images
函数时就加载的类,称为非懒加载类
- 通俗理解就是当某个类实现了
+load
函数,则就是非懒加载类
,没有实现就是懒加载类
4.2 懒加载类
分析
对于LGPerson类不实现+load
函数,我们从前面分析知道_read_images
中有处理非懒加载类
代码,我们打上断点以及打印,如图由于LGPerson没有实现+load
函数,所以是懒加载类,因此没有进入我们的断点。然而LGPerson
类在什么时候加载了呢?我们研究 非懒加载类
的代码发现,实现了realizeClassWithoutSwift
函数,因此我们看懒加载类
会不会进入这个函数。
4.2.1 realizeClassWithoutSwift
从这里我们验证了懒加载类
的类的加载延迟到第一次消息发送
4.3 非懒加载类
分析
我们让LGPerson
实现+load
函数,里面什么也不用做,如图此时看_read_images
中有关非懒加载类
的处理此时我们看到进入了我们原先的断点中,且也执行了realizeClassWithoutSwift
函数
4.4 重点分析realizeClassWithoutSwift
函数
重点代码分析