咱们从iOS底层原理之—dyld与objc的关联中知道dyld关联objc
时,经过_read_images
函数中的readClass
函数来读取类的信息并将类关联起来,咱们本文主要来探讨类的加载。markdown
_read_images
函数因为咱们探究的是类的加载,且在iOS底层原理之—dyld与objc的关联中咱们已经主要分析了下_read_images
函数的做用,所以咱们主要看_read_images
函数中有关的部分代码,上代码app
// 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");
复制代码
readClass
部分代码readClass
以前cls分析在没有调用readClass
时,咱们读取cls信息从上图中咱们发现此时cls只是一串地址函数
readClass
以后cls分析调用readClass
以后,咱们读取cls信息从上图中咱们得知cls已经由一串地址变成了LGPerson类。所以咱们得出必定是
readClsss
函数作了什么,咱们来分析下readClsss
函数。post
readClsss
函数分析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
类型的代码ui
经过代码调试,最终执行了addNamedClsss
函数在执行
addNamedClsss
函数以前打印cls依然是一串地址。当执行完addNamedClsss
函数时,打印cls发现指向了LGPerson
所以咱们得出
addNamedClsss
对cls进行了指向操做。this
addNamedClsss
函数懒加载类
:推迟到第一次消息发送的时候才加载的类称为懒加载类
;非懒加载类
:在map_images
函数执行_read_images
函数时就加载的类,称为非懒加载类
+load
函数,则就是非懒加载类
,没有实现就是懒加载类
懒加载类
分析对于LGPerson类不实现+load
函数,咱们从前面分析知道_read_images
中有处理非懒加载类
代码,咱们打上断点以及打印,如图因为LGPerson没有实现
+load
函数,因此是懒加载类,所以没有进入咱们的断点。然而LGPerson
类在何时加载了呢?咱们研究 非懒加载类
的代码发现,实现了realizeClassWithoutSwift
函数,所以咱们看懒加载类
会不会进入这个函数。spa
realizeClassWithoutSwift
从这里咱们验证了
懒加载类
的类的加载延迟到第一次消息发送
ssr
非懒加载类
分析咱们让LGPerson
实现+load
函数,里面什么也不用作,如图此时看
_read_images
中有关非懒加载类
的处理此时咱们看到进入了咱们原先的断点中,且也执行了
realizeClassWithoutSwift
函数3d
realizeClassWithoutSwift
函数重点代码分析 调试