OC底层原理13 - 类的加载过程

类的加载

类的加载过程主要涉及两个重要函数:map_imagesload_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_allocallocWithZone -> 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));方式,即直接通过函数指针调用的方式.

你可能感兴趣的:(OC底层原理13 - 类的加载过程)