类的加载
类的加载过程主要涉及两个重要函数:map_images
和load_images
。
map_images
:主要是管理文件中和动态库中的所有符号,如class、protocol、selector、category等。
load_images
:加载执行load方法。
map_images的源码流程
-
map_images
函数源码
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);
}
- 进入
map_images_nolock
源码,以下代码省略部分代码
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);
}
firstTime = NO;
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
以上函数是,从MachO
文件中找出所有的镜像文件,调用_read_images
函数,将MachO
中的类信息加载至内存中.因此接下来研究的重点是_read_images
函数.
_read_images
函数
- [步骤1]根据
MachO(镜像文件)
中类的数量创建一张哈希表(gdb_objc_realized_classes)
,哈希表的大小=类的数量*4/3.注意:该哈希表用于存储已经命名但不在共享缓存中的类.
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.
# if TARGET_OS_OSX
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
//遍历镜像文件,找到主程序的镜像文件,查看镜像文件中的__DATA段是否有__objc_rawisa标志,若存在禁用non-pointer isa.
for (EACH_HEADER) {
if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)){
DisableNonpointerIsa = true;
//省略打印信息
}
break; // assume only one MH_EXECUTE image
}
# endif
#endif
if (DisableTaggedPointers) {
disableTaggedPointers();
}
initializeTaggedPointerObfuscator();
//省略打印信息
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
//当应用程序第一次加载时,创建一张哈希表gdb_objc_realized_classes,该表用于存储不在共享缓存但已命名的类,哈希表的大小为类数量的4/3
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
- [步骤2]修复预编译阶段
@selector
的混乱问题,例如,多个框架中都存在init函数,在系统中读取方法Core Foundation框架中init方法的地址 = Core Foundation框架在主程序中的偏移 + init方法在Core Foundation框架中的偏移.因此,需要对@selector进行适当调整.
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);//__objc_selrefs
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
- [步骤3]修复未解析的类.从MachO中
__objc_classlist
段加载类列表
,遍历列表中的类,进行readClass
.如果readClass
的结果与类列表
中的类不同,则进行修复操作.不过一般不会出现这种情况,只有类被移动并且没有被删除才会出现.在readClass
中,从cls->mangledName()
中得到类的名字,将类的名字与地址进行关联后,插入至gdb_objc_realized_classes
中,同时将该类以及元类插入到allocatedClasses
表。在这一步中,readClass
将类的地址和名字
加载到了内存中.
// 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;
}
if (hi->mhdr()->filetype == MH_EXECUTE){
printf("%d\n",__LINE__);
}
//__objc_classlist
//从Mach-O中获取静态段__objc_classlist,是一个classref_t类型的指针
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
//根据地址,依次取出类的指针,转成类Class,即objc_class结构体指针.
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;
}
}
}
- [步骤4]修复重映射的类.如果存在没有被镜像文件加载进来的类,则在此时进行重映射。
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
- [步骤5]修复一些旧的
objc_msgSend_fixup
调用.主要对一些旧的消息修复进行强制修复工作,如alloc -> objc_alloc
、allocWithZone -> objc_allocWithZone
等。
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
//省略打印代码
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
- [步骤6]当类里面有协议时,通过
readProtocol
加载协议内容至内存.创建存储proctol
的哈希表
,从MachO
中__objc_protolist
段中读取协议列表,遍历协议列表,进行readProtocol
,将协议添加到proctol表,注册到内存。
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
if (launchTime && isPreoptimized) {
//省略打印代码
continue;
}
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
- [步骤7]修复没有被加载的协议.预先优化的镜像文件可能已经有了正确的protocol引用,但是我们并不能确定,所以这里进行一次修复工作,以防被引用的协议被重新分配。
for (EACH_HEADER) {
if (launchTime && hi->isPreoptimized())
continue;
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
- [步骤8]加载分类.注意:
这里并不会加载分类
,只有在didInitialAttachCategories赋值为true之后才会执行,而didInitialAttachCategories赋值为true的过程是在_dyld_objc_notify_register的调用完成后的第一个load_images调用时赋值的。
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
- [步骤9]加载非懒加载的类.非懒加载的类即实现了load方法的类.从
MachO
中__objc_nlclslist
段中读取非懒加载类列表,执行addClassTableEntry(cls);
方法将非懒加载类插入到类表,加载到内存。在[步骤3]中,我们加载到内存的类已有了地址和名字,最后执行realizeClassWithoutSwift对类的结构进行完善。
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
- [步骤10]realize future classes.如果存在被处理的
future class
,则需要在这里实现,以防CF操作它们。这里的实现也是通过realizeClassWithoutSwift
.
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
从上面分析看,在类的加载过程中,较为重要的两个步骤为[步骤3]的readClass
和[步骤9]的realizeClassWithoutSwift
.接下来逐一分析.
readClass
函数
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->nonlazyMangledName();
//如果继承链中存在父类缺失或者weak-linked情况,直接忽略这个类, return nil
if (missingWeakSuperclass(cls)) {
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->setSuperclass(nil);
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (mangledName != nullptr) {
// 如果这个类是一个早先分配的作为将来要处理的类,那么将objc_class数据复制到future class,保存future的rw数据块
if (Class newCls = popFutureNamedClass(mangledName)) {
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));
// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
}
// 如果是预优化的类 并且 不是future class, 则 ASSERT,
if (headerIsPreoptimized && !replacing) {
ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
// 关联地址与名字 加入gdb_objc_realized_classes表
addNamedClass(cls, mangledName, replacing);
} else {
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
// 加入allocatedClasses表
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;
}
从源码中可以看出readClass
主要完成了以下工作:
- 通过
cls->nonlazyMangledName();
获取类的名字 - 判断继承链中是否存在父类缺失或者weak-linked的情况,如果存在,说明该类是不完整的,因此直接忽略返回nil
- 判断当前以
mangledName
命名的类是否是一个早先被分配为将来要处理的类,如果是的话,则将当前类的整个objc_class结构体
数据复制到future class结构体中
,并保存future的rw数据块 - 如果当前类在共享缓存中,则此处ASSERT,否则
- 执行
addNamedClass
关联类的地址与名字
,并插入至gdb_objc_realized_classes
表. - 执行
addClassTableEntry
将当前类
插入至allocatedClasses
表;
- 执行
经过以上几个步骤,MachO中的类已经加载至内存中,并且有了类名和地址.但类中的数据还没有关联,接下来看realizeClassWithoutSwift
函数,该函数完成类的数据关联.
realizeClassWithoutSwift
函数
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
// 如果类已实现,则直接返回
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
cls->chooseClassArrayIndex();
//省略打印信息代码
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
//NONPOINTER_ISA的处理,在分析类加载时该部分不作为分析重点
#endif
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls, previously);
return cls;
}
从源码中可以看出,realizeClassWithoutSwift
完成了以下工作:
- 从
cls
中读取data
数据,并将读出的数据强制转换成(const class_ro_t *)
类型.- 如果当前
cls
是future class,则将data
数据直接赋值给rw
. - 如果当前
cls
是Normal class,则开辟class_rw_t
大小空间rw
,对rw
中的ro
进行赋值.并将data
指向刚开辟的rw
.
- 如果当前
- 递归实现
当前类的父类
以及元类
,随后更新该类的父类和元类以备重新映射。确保继承链以及isa链的完整性. - 从
ro
中赋值一些标志到rw
中. - 如果父类存在,则将这个类链接到它的父类的子类列表,否则作为一个新的根类.
- 执行methodizeClass.
methodizeClass
函数
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
//省略打印代码
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously, ATTACH_METACLASS);
} else {
objc::unattachedCategories.attachToClass(cls, previously, ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
methodizeClass方法中,主要做了这些事情:
- install 类的方法列表(method_list_t),从
ro
中读取baseMethods
,如果列表有值,则对baseMethods
中的方法进行排序,排序方法(SortBySELAddress):按照类SEL
的先后顺序. - install 类的属性列表(property_list_t),如果
rwe
已创建,且属性列表有值,则调用attachLists(&proplist, 1)
. - install 类的协议列表(protocol_list_t),如果
rwe
已创建,且协议列表有值,则调用attachLists(&protolist, 1)
. - Attach categories.
Attach categories
是添加分类的过程,这个后续再分析,此时暂时将该部分内容挂起.
prepareMethodLists中的方法排序
首先从源码进行分析
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
struct SortBySELAddress :
public std::binary_function
{
bool operator() (const struct method_t::big& lhs,
const struct method_t::big& rhs)
{ return lhs.name < rhs.name; }
};
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
从源码中可以看到,对方法列表
排序的依据:
* 若方法的SEL不同,则按SEL的从小到大进行排序
.
* 若方法的SEL相同,则保留加入至方法列表的先后顺序
.
接下来通过lldb验证
排序前:
(lldb) p/x (mlist->begin()+0)->name()
(SEL) $1 = 0x0000000100003f36 "instanceHQBMethod2"
(lldb) p/x (mlist->begin()+1)->name()
(SEL) $2 = 0x0000000100003e9b "instanceMethod1"(HQPerson(HQB))
(lldb) p/x (mlist->begin()+2)->name()
(SEL) $3 = 0x0000000100003f49 "instanceHQBMethod1"
(lldb) p/x (mlist->begin()+3)->name()
(SEL) $4 = 0x0000000100003eab "instanceHQAMethod2"
(lldb) p/x (mlist->begin()+4)->name()
(SEL) $5 = 0x0000000100003e9b "instanceMethod1"(HQPerson(HQA))
(lldb) p/x (mlist->begin()+5)->name()
(SEL) $6 = 0x0000000100003ebe "instanceHQAMethod1"
(lldb) p/x (mlist->begin()+6)->name()
(SEL) $7 = 0x0000000100003eee "instancePersonMethod2"
(lldb) p/x (mlist->begin()+7)->name()
(SEL) $8 = 0x0000000100003e9b "instanceMethod1"(HQPerson)
(lldb) p/x (mlist->begin()+8)->name()
(SEL) $9 = 0x0000000100003f04 "instancePersonMethod1"
(lldb) p/x (mlist->begin()+9)->name()
(SEL) $10 = 0x00007fff795749a1 "name"
(lldb) p/x (mlist->begin()+10)->name()
(SEL) $11 = 0x00007fff79580d69 "setName:"
(lldb) p/x (mlist->begin()+11)->name()
(SEL) $12 = 0x00007fff79573b90 ".cxx_destruct"
排序后:
(lldb) p/x (mlist->begin()+0)->name()
(SEL) $24 = 0x0000000100003e9b "instanceMethod1"(HQPerson(HQB))
(lldb) p/x (mlist->begin()+1)->name()
(SEL) $25 = 0x0000000100003e9b "instanceMethod1"(HQPerson(HQA))
(lldb) p/x (mlist->begin()+2)->name()
(SEL) $26 = 0x0000000100003e9b "instanceMethod1"(HQPerson)
(lldb) p/x (mlist->begin()+3)->name()
(SEL) $27 = 0x0000000100003eab "instanceHQAMethod2"
(lldb) p/x (mlist->begin()+4)->name()
(SEL) $28 = 0x0000000100003ebe "instanceHQAMethod1"
(lldb) p/x (mlist->begin()+5)->name()
(SEL) $29 = 0x0000000100003eee "instancePersonMethod2"
(lldb) p/x (mlist->begin()+6)->name()
(SEL) $30 = 0x0000000100003f04 "instancePersonMethod1"
(lldb) p/x (mlist->begin()+7)->name()
(SEL) $31 = 0x0000000100003f36 "instanceHQBMethod2"
(lldb) p/x (mlist->begin()+8)->name()
(SEL) $32 = 0x0000000100003f49 "instanceHQBMethod1"
(lldb) p/x (mlist->begin()+9)->name()
(SEL) $33 = 0x00007fff79573b90 ".cxx_destruct"
(lldb) p/x (mlist->begin()+10)->name()
(SEL) $34 = 0x00007fff795749a1 "name"
(lldb) p/x (mlist->begin()+11)->name()
(SEL) $35 = 0x00007fff79580d69 "setName:"
map_images
总结
至止,类的加载过程(分类除外)大致完成.map_images
的主要工作可大概描述为以下:
- 通过
readClass
将类的地址与名字进行关联,并载入至内存中 - 通过
realizeClassWithoutSwift
函数对类的数据进行关联,完成ro和rw
的赋值等操作 - 通过
methodizeClass
函数对类的方法/属性/协议等进行处理.
load_images
的源码流程
-
load_images
源码
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
// 现在所有的+load方法
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
//调用所有的+load方法
call_load_methods();
}
load_images
主要是完成以下工作:
* 通过prepare_load_methods
方法找到当前可执行文件中的所有+load
方法
* 通过call_load_methods
执行前一步所找到的+load
方法
prepare_load_methods
源码
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
//查找MachO文件中__objc_nlclslist(OC实现了+load方法)列表.
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//查找当前类,以及继承链中所有实现+load方法的类,并将找到的类和+load方法的IMP加入至loadable_classes表中
schedule_class_load(remapClass(classlist[i]));
}
//查找MachO文件中__objc_nlcatlist(记录分类中实现+load方法)的列表
//将分类和+load方法的IMP加入至loadable_categories表中
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
call_load_methods
源码
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
// 调用类的 +load,直到所有类的+load方法都被调用
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
// 调用分类中的+load方法
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
// 如果有类或更多未尝试的分类,则运行更多的+load
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
load_images
总结
根据
MachO文件
查找所有实现了+load方法的类
.当找到的类的+load方法
时,将类的+load方法的IMP
和类
添加至loadable_classes
.
注意:当父类
和子类
同时都实现了+load方法
时,根据schedule_class_load函数源码(schedule_class_load(cls->getSuperclass())
)可知,schedule_class_load函数是递归调用
的,因此父类的+load方法先添加入loadable_classes列表
中.子类的+load方法后添加入loadable_classes列表
.根据
MachO文件
查找所有实现了+load方法的分类
.当找到的分类的+load方法
时,将分类的+load方法的IMP
和分类
添加至loadable_categories
先遍历
loadable_classes
列表,此列表中存储是的主类的+load方法
,依次执行loadable_classes
列表中的+load方法
.-
其次遍历
loadable_categories
列表,此列表中存储是的分类的+load方法
,依次执行loadable_categories
列表中的+load方法
.注意:不管主类还是分类,在执行+load方法时,使用的是
(*load_method)(cls, @selector(load));
方式,即直接通过函数指针
调用的方式.