ios底层-类的加载上 核心方法分析

咱们一般的开发过程,对于类的使用能够说是信手拈来,初始化、调用方法等等。那么类是何时进行加载的呢?在app启动以后,若是实现了+load方法,会先执行load方法,那么load方法又是什么时候执行的呢?经过研究类的加载,能够获得问题的答案。swift

libobjc:781数组

Xcode 11.6缓存

objc_init

dyld启动流程一文中,有提到过libobjc的初始化方法_objc_initmarkdown

  • environ_init() : 读取影响运⾏时的环境变量。若是须要,还能够打印环境变量帮助。
  • tls_init() 关于线程key的绑定 - ⽐如每线程数据的析构函数
  • static_init() 运⾏C ++静态构造函数。在dyld调⽤咱们的静态构造函数以前,libc 会调⽤ _objc_init(),所以咱们必须⾃⼰作
  • runtime_init() : runtime运⾏时环境初始化,⾥⾯主要是:unattachedCategories,allocatedClasses的初始化,后⾯会分析
  • exception_init () 初始化libobjc的异常处理系统
  • cache_init() 缓存条件初始化
  • _imp_implementationWithBlock_init :启动回调机制。一般这不会作什么,由于全部的初始化都

是惰性的,可是对于某些进程,咱们会火烧眉毛地加载trampolines dylibapp

  • _dyld_objc_notify_register: 咱们向dyld注册了三个回调,分别是:map_iamges

load_iamgesunmap_imageside

咱们重点关注前两个方法。函数

map_images

在当前方法中打下断点,能够发现map_iamges的调用是在dyld的源码之中, oop

具体的代码逻辑能够在dyld源码中进行查看。 咱们沿着map_iamges接下往里面走,执行了map_images_nolock方法。核心方法以下:post

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    ...
    // Find all images with Objective-C metadata.
    hCount = 0;
    // 经过mach_header遍历,找出全部的类数量
    // Count classes. Size various table based on the total.
    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
    {
        uint32_t i = mhCount;
        while (i--) {
            const headerType *mhdr = (const headerType *)mhdrs[i];
			// 生成对应的header_info
            auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
            if (!hi) {
                // no objc data in this entry
                continue;
            }
            ...
           	//添加到hList中
            hList[hCount++] = hi;
            ...
        }
    }
    ...
    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    ...
    // Call image load funcs after everything is set up.
    for (auto func : loadImageFuncs) {
        for (uint32_t i = 0; i < mhCount; i++) {
            func(mhdrs[i]);
        }
    }
}
复制代码

_read_images

_read_images方法主要作了以下操做: ui

咱们先来关注一下第9个,类的加载处理

// 实现非懒加载类(+load方法和静态实例)
    // Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
    	// 遍历各个header_info,从对应的macho中读取出非懒加载类的列表
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            
            if (!cls) continue;
            // 添加cls到
            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
            }
            // 实现cls
            realizeClassWithoutSwift(cls, nil);
        }
    }
复制代码

realizeClassWithoutSwift

/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls, 
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class. 
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
// 类cls的第一次初始化,包括开辟它的可读写数据class_rw_t,返回类的真正结构
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) return cls;
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    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.
        // 开辟class_rw_t尺寸大小的空间
        rw = objc::zalloc<class_rw_t>();
        // 赋值rw->ro
        rw->set_ro(ro);
        // 修改rw中相关标识flags
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        // 赋值cls—>rw
        cls->setData(rw);
    }

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // 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), nil);
    // 递归实现元类
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->name, "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    // 更新父类和本身的ISA指向即元类
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    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
    // 从ro中拷贝一些标识符到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.
    // 若是ro中禁止了关联对象或者父类禁止了关联对象,修改rw相关标识
    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;
}

复制代码

咱们先来看一下关于类的结构定义:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits; 
}
复制代码

在上述代码中读取ro的代码为:auto ro = (const class_ro_t *)cls->data()

class_rw_t* data() const {
     return (class_rw_t *)(bits & FAST_DATA_MASK);
}
复制代码

在源码中data()返回的实际上是rw的指针。咱们知道ro是在编译期就肯定下来的,能够理解为在未实现的cls结构中,本来属于rw的位置存放的是ro的指针。

1. 实现类的第一步操做就是开辟rw,正确创建ro和rw以及cls自己的关系

2. 递归实现当前cls的父类和元类

3. 将ro中的一些标识拷贝到rw中

4. 修正cls中的方法、协议、属性以及未连接的分类

methodizeClass

/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
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
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }
    
    // 装载cls本身实现的方法和属性
    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        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);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        // 根元类增长initialize的实现
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    // 在特定状况下若是有未加载的分类,加载到cls中,具体的场景后续会讲到
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
...
}
复制代码

接下来看一下methodizeClass,它的主要做用是调整cls实现的方法列表、属性列表以及协议列表以及加载特定状况下的分类。

prepareMethodLists

在该方法中,会处理cls的方法列表。

static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle)
{
    runtimeLock.assertLocked();
    if (addedCount == 0) return;
	...
    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        // Fixup selectors if necessary
        if (!mlist->isFixedUp()) {
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
        }
    }
   	...
}
复制代码

遍历二维数组addedLists,取出其中的每个list进行fixupMethodList修复。

static void 
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
    ...
    if (!mlist->isUniqued()) {
        mutex_locker_t lock(selLock);
    	// 惟一标识list中的method
        // Unique selectors in list.
        for (auto& meth : *mlist) {
            const char *name = sel_cname(meth.name);
            meth.name = sel_registerNameNoLock(name, bundleCopy);
        }
    }

    // Sort by selector address.
    // 根据selector地址进行排序,这个能够解释在懒加载状况下分类和主类的同名方法是排在一块儿的
    if (sort) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(mlist->begin(), mlist->end(), sorter);
    }
    
    // Mark method list as uniqued and sorted
    mlist->setFixedUp();
}
复制代码

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();
}
复制代码

loadAllCategories

static void loadAllCategories() {
    mutex_locker_t lock(runtimeLock);

    for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        load_categories_nolock(hi);
    }
}
复制代码

遍历所有的header数据,加载其中的分类。

load_categories_nolock

static void load_categories_nolock(header_info *hi) {
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;
    // processCatlist相似于一个函数指针,会在该方法的末尾进行调用
    auto processCatlist = [&](category_t * const *catlist) {
        for (unsigned i = 0; i < count; i++) {
        // 遍历catlist获得当前的分类cat
            category_t *cat = catlist[i];
            // 获取cat对应的cls
            Class cls = remapClass(cat->cls);
            // 初始化lc结构体
            // struct locstamped_category_t {
 	    //    category_t *cat;
    	    //    struct header_info *hi;
	    //  };
            locstamped_category_t lc{cat, hi};
	    ...

            // Process this category.
            if (cls->isStubClass()) {
                ...
            } else {
            	// 先将category注册到它对应的class中,若是class已经实现则重构class的方法列表,由于此时方法列表发生了改变,若是class尚未实现只将当前category和class添加联系。同时还判断了类方法和实例方法
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    if (cls->isRealized()) {
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }
                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
                    if (cls->ISA()->isRealized()) {
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };
	// 从macho中读取分类列表以及更新分类个数count
    processCatlist(_getObjc2CategoryList(hi, &count));
    processCatlist(_getObjc2CategoryList2(hi, &count));
}
复制代码

attachCategories

// 将分类加载到主类
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
   	...
    // 初始化必要的数组,默认最多会有64个分类
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    // 存放method_list_t的数组
    method_list_t   *mlists[ATTACH_BUFSIZ];
    // property_list_t的数组
    property_list_t *proplists[ATTACH_BUFSIZ];
    // protocol_list_t的数组
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    // 获取rwe或者开辟rwe
    auto rwe = cls->data()->extAllocIfNeeded();

    for (uint32_t i = 0; i < cats_count; i++) {
        // 读取locstamped_category_t结构体
        auto& entry = cats_list[i];
		// 获取cat对应的类方法或者实例方法
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
        	// 若是数组满了,先修复cls方法,而后重置mcount
            if (mcount == ATTACH_BUFSIZ) {
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}

复制代码

attachLists

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            // 原始的数组个数
            uint32_t oldCount = array()->count;
            // 新的数组个数
            uint32_t newCount = oldCount + addedCount;
            // 开辟新数组个数大小的空间
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            // 从新赋值
            array()->count = newCount;
            // 将原有的数组日后平移插入的元素个数的大小
            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]));
        }
    }
复制代码

该方法主要负责将新的列表插入到原始列表的首位置,其余元素日后平移。

extAlloc

class_rw_ext_t是苹果在wwdc20提出的一个新的改动,主要是为了减小类所占用的内存,结构以下:

struct class_rw_ext_t {
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};
复制代码

该方法的做用是开辟class_rw_ext_t而且将ro中的方法、属性以及协议列等数据复制到rwe的对应位置。

class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
    runtimeLock.assertLocked();
	开辟class_rw_ext_t大小的内容空间
    auto rwe = objc::zalloc<class_rw_ext_t>();
    
    rwe->version = (ro->flags & RO_META) ? 7 : 0;

    method_list_t *list = ro->baseMethods();
    if (list) {
    // 复制一份ro中的方法列表存储到rwe的methods中
        if (deepCopy) list = list->duplicate();
        rwe->methods.attachLists(&list, 1);
    }
    // 复制方法列表
    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }
	//复制协议列表
    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }
	// 正确处理rwe和ro的关系
    set_ro_or_rwe(rwe, ro);
    return rwe;
}
复制代码

prepare_load_methods

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();
	// 读取当前macho中的非懒加载类列表
    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
    	// 添加列表中每个cls以及load方法的实现到一个数组中
        schedule_class_load(remapClass(classlist[i]));
    }
	
    // 获取非懒加载分类的列表
    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);
    }
}
复制代码

schedule_class_load

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    ASSERT(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;
	// 递归添加,先添加父类,再添加子类,这样能够确保父类的load方法在子类的load方法以前调用
    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
复制代码

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
        while (loadable_classes_used > 0) {
        // 调用主类的load方法
            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
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

复制代码
相关文章
相关标签/搜索