OC底层探索(十三)_objc_init分析——类的加载(下)

OC底层文章汇总

在文章OC底层探索(十二)_objc_init分析——类的加载(上)中,有介绍到_read_Image的readClass方法是没有对rw(read write)、ro(read only)、rwe(read write ext)进行操作,那么在哪对类进行操的呢?

类的加载

rw、ro和rwe

  • app在使用类时,是需要在磁盘中app的二进制文件中读取类的信息,二进制文件中的类存储了类的元类、父类、flags和方法缓存,如下图
    在这里插入图片描述

  • 那么类的额外信息(name、方法、协议和实例变量等)存储在class_ro_t中

  • class_ro_t简称ro:read only,将类从磁盘中读取到内存中就是对ro的赋值操作。由于ro是只读的,加载到内存后不会发生改变又称为clean memory(干净内存)。

  • class_rw_t简称rw:read write,用于读写编写程序。 drity memory 在进程运行时发生更改的内存。类一经使用运行时就会分配一个额外的内存,那么这个内存变成了drity memory。但是在实际应用中,类的使用量只是10%,这样就在rw中造成了内存浪费,所以苹果就把rw中方法、协议和实例变量等放到了class_rw_ext_t中。
    在这里插入图片描述
    👇
    在这里插入图片描述

  • class_rw_ext_t简称rwe:read write ext,用于运行时存储类的方法、协议和实例变量等信息。

  • 下图就是类在内存中存储的结构
    在这里插入图片描述

分析如何、何时对ro、rw、rwe进行操作的

  • 在_read_image源码中有一段代码是下面的内容,其注释是非懒加载类加载方法。
 // +load handled by prepare_load_methods()

    // Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            
            const char *mangledName  = cls->mangledName();
            const char *LGPersonName = "LGPerson";
           
            if (strcmp(mangledName, LGPersonName) == 0) {
                auto kc_ro = (const class_ro_t *)cls->data();
                printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",LGPersonName);
            }
            
            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);
        }
    }

    ts.log("IMAGE TIMES: realize non-lazy classes");

    // Realize newly-resolved future classes, in case CF manipulates them
    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);
    }
  • 在iOS开发中,类都是懒加载的,只有用到类的时候才会将类加载到内存中,那么如何将类变成非懒加载类呢?在类中添加load方法
 + (void)load{
}

realizeClassWithoutSwift 方法:实现类

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;
    
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";

    if (strcmp(mangledName, LGPersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data();
        auto kc_isMeta = kc_ro->flags & RO_META;
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
        }
    }

    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?

    //part1.在磁盘中将类的数据取出放入ro中
    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中
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }



    // 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.
    //part2:实现父类和元类的realizeClass 形成一个继承链
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
		
		······


    // Attach categories
    //part3:实现类的方法
    methodizeClass(cls, previously);

    return cls;
}
  • part1.在磁盘中将类的数据取出放入ro中,并且将数据也存储到rw中一份
 //part1.在磁盘中将类的数据取出放入ro中
    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中
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }
  • part2:实现父类和元类的realizeClass 形成一个继承链
    递归循环将类的父类和元类都实现realizeClassWithoutSwift方法,从而形成一个继承链。
 supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
 metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);		
  • part3:实现类的方法
    将类的方法等读取到rwe中去。
    methodizeClass(cls, previously);

methodizeClass方法

  • methodizeClass方法中对rwe进行了操作,但没有赋值,并且将方法进行了排序
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();
    
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";

    if (strcmp(mangledName, LGPersonName) == 0) {
        bool kc_isMeta = cls->isMetaClass();
        auto kc_rw = cls->data();
        auto kc_ro = kc_rw->ro();
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
        }
    }

    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    // 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
        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);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods()) {
        if (PrintConnecting) {
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name));
        }
        ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name); 
    }
#endif
}
  • prepareMethodLists:方法排序
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle)
{
    runtimeLock.assertLocked();
    
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";

    if (strcmp(mangledName, LGPersonName) == 0) {
        bool kc_isMeta = cls->isMetaClass();
        auto kc_rw = cls->data();
        auto kc_ro = kc_rw->ro();
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
        }
    }
    

    if (addedCount == 0) return;

    // There exist RR/AWZ/Core special cases for some class's base methods.
    // But this code should never need to scan base methods for RR/AWZ/Core:
    // default RR/AWZ/Core cannot be set before setInitialized().
    // Therefore we need not handle any special cases here.
    if (baseMethods) {
        ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
    }

    // Add method lists to array.
    // Reallocate un-fixed method lists.
    // The new methods are PREPENDED to the method list array.

    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        ASSERT(mlist);

        // Fixup selectors if necessary
        if (!mlist->isFixedUp()) {
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
        }
    }

    // If the class is initialized, then scan for method implementations
    // tracked by the class's flags. If it's not initialized yet,
    // then objc_class::setInitialized() will take care of it.
    if (cls->isInitialized()) {
        objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
    }
}

👇
fixupMethodList方法

static void 
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
    runtimeLock.assertLocked();
    ASSERT(!mlist->isFixedUp());

    // fixme lock less in attachMethodLists ?
    // dyld3 may have already uniqued, but not sorted, the list
    if (!mlist->isUniqued()) {
        mutex_locker_t lock(selLock);
    
        // 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.
    if (sort) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(mlist->begin(), mlist->end(), sorter);
    }
    
    // Mark method list as uniqued and sorted
    mlist->setFixedUp();
}
  • 在获取到方法、属性和协议后,并没有对rwe赋值
    在这里插入图片描述

懒加载类与非懒加载类

在上面我们探索_read_image源码中,我们注意到了注释显示非懒加载类,执行ro、rw、rwe的赋值,那么懒加载在什么时候执行的呢?

  • 1.删除load方法,将类变成懒加载类,
    在这里插入图片描述

  • 2.在methodizeClass方法打个断点

  • 3.打印堆栈查看方法的调用,就会发现在执行loopUpImpForward时执行的methodizeClass方法。
    在这里插入图片描述

  • 验证

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
   ......
//当类没有实现时,需要实现类
    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
  
    }


  ......
    return imp;
}

👇

  • initializeAndLeaveLocked方法
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
    return initializeAndMaybeRelock(cls, obj, lock, true);
}

👇

  • initializeAndMaybeRelock方法
static Class initializeAndMaybeRelock(Class cls, id inst,
                                      mutex_t& lock, bool leaveLocked)
{
   	......
        nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
        // runtimeLock is now unlocked
        // fixme Swift can't relocate the class today,
        // but someday it will:
        cls = object_getClass(nonmeta);
    }

👇

  • realizeClassMaybeSwiftAndUnlock方法
static Class
realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock)
{
    return realizeClassMaybeSwiftMaybeRelock(cls, lock, false);
}

👇

  • realizeClassMaybeSwiftMaybeRelock方法
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
   	......
        realizeClassWithoutSwift(cls, nil);
    ......

    return cls;
}
  • 最终执行到了 realizeClassWithoutSwift方法,所以懒加载类是在使用到类(初始化消息发送)的时候进行的加载

总结

懒加载类的加载流程:
_read_images -> lookUpImpOrForward -> realizeClassWithoutSwift -> methodizeClass
非懒加载类的加载流程:
_read_images -> realizeClassWithoutSwift -> methodizeClass

分类的加载

在realizeClassWithoutSwift方法中我们可以发现好多注释都是 Attach categories,那么分类是什么结构呢?

分类的本质

  • 在程序中新建分类,并且clang,生成main.cpp文件,查看分类的结构:分类也是一个结构体。

在这里插入图片描述

  • 在objc_4的源码中搜索category_t也可以看到category的源码
    在这里插入图片描述

分类的加载流程

  • attachToClass方法
 void attachToClass(Class cls, Class previously, int flags)
    {
        runtimeLock.assertLocked();
        ASSERT((flags & ATTACH_CLASS) ||
               (flags & ATTACH_METACLASS) ||
               (flags & ATTACH_CLASS_AND_METACLASS));
               
        auto &map = get();
        auto it = map.find(previously);
        if (it != map.end()) {//主类没有实现 - 分类实现了load,就会执行到这
            category_list &list = it->second;
            if (flags & ATTACH_CLASS_AND_METACLASS) {
                int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
                attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
                attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
            } else {
                attachCategories(cls, list.array(), list.count(), flags);
            }
            map.erase(it);
        }
    }

👇

  • attachCategories方法
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }

    /*
     * Only a few classes have more than 64 categories during launch.
     * This uses a little stack, and avoids malloc.
     *
     * Categories must be added in the proper order, which is back
     * to front. To do that with the chunking, we iterate cats_list
     * from front to back, build up the local buffers backwards,
     * and call attachLists on the chunks. attachLists prepends the
     * lists, so the final result is in the expected order.
     */
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    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);
    auto rwe = cls->data()->extAllocIfNeeded(); 
    
    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            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);
}
  • 其中我们发现rwe的赋值 rwe = cls->data()->extAllocIfNeeded();
    在源码中我们发现extAllocIfNeeded -> extAlloc中对rwe进行了申请内存空间,并进行了赋值。
class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>();
        } else {
            return extAlloc(v.get<const class_ro_t *>());
        }
    }
👇
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
    runtimeLock.assertLocked();

    auto rwe = objc::zalloc<class_rw_ext_t>();

    rwe->version = (ro->flags & RO_META) ? 7 : 0;

    method_list_t *list = ro->baseMethods();
    if (list) {
        if (deepCopy) list = list->duplicate();
        rwe->methods.attachLists(&list, 1);
    }

    // See comments in objc_duplicateClass
    // property lists and protocol lists historically
    // have not been deep-copied
    //
    // This is probably wrong and ought to be fixed some day
    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);
    }

    set_ro_or_rwe(rwe, ro);
    return rwe;
}

👇
在分析类的加载时,分析到了在methodizeClass方法中没有对rwe进行赋值,那么我们全局搜索一下extAllocIfNeeded方法的调用。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
通过全局搜索能够看出rwe除了在添加分类以外,还在除了系统外的addMethod,addProtocol,addProperty这些地方用到过,正如WWDC里面所说的只有对原始的内存进行处理和修改的时候我们才会用到rwe。

  • 其中我们只分析method_list_t ,(属性和协议都和方法类似)
    在这里插入图片描述
    👇

将分类的方法列表存储到mlists数组中。判断mcount是否等于ATTACH_BUFSIZ,表示最大修改次数为64次, 如果不等于则mlists[64 - 1] = mlist进行倒序插入

在这里插入图片描述
👇
然后执行attachLists方法
在这里插入图片描述

  • attachLists方法:将分类加载到主类上
void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) { //目前有多个list,又加入多个list
            // 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;
            //将存在的多个list整段平移
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
             //将新的lists存放到首位
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            //part2:向list数组中添加一个list,一维数组
            list = addedLists[0];
        } 
        else {//
            // 1 list -> many lists
            //part3:向list数组(目前只有一个元素)中添加多个list(list数组),二维数组
            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;
            //将oldlist放到最后面的位置
            if (oldList) array()->lists[addedCount] = oldList;
            //从0位置放addedLists,大小是addedCount * sizeof(array()->lists[0])
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

    void tryFree() {
        if (hasArray()) {
            for (uint32_t i = 0; i < array()->count; i++) {
                try_free(array()->lists[i]);
            }
            try_free(array());
        }
        else if (list) {
            try_free(list);
        }
    }

    template<typename Result>
    Result duplicate() {
        Result result;

        if (hasArray()) {
            array_t *a = array();
            result.setArray((array_t *)memdup(a, a->byteSize()));
            for (uint32_t i = 0; i < a->count; i++) {
                result.array()->lists[i] = a->lists[i]->duplicate();
            }
        } else if (list) {
            result.list = list->duplicate();
        } else {
            result.list = nil;
        }

        return result;
    }
};
  • part1:目前有多个list,又加入多个list
    lists中存在多个list,这时又要插入多个list,那么

     a. 生成newCount:总的list个数
     b. 根据newCount创建一个新的Array
     c. memmove:将oldList整段平移
     d.memcpy:将新的lists放到首位memcpy(参数1:从什么位置,参数2:插入什么,参数3:插入的长度)
    
  • part2:向list数组中添加一个list,一维数组

     a. 想list数组中添加一个list
    
  • part3:向list数组(目前只有一个元素)中添加多个list(list数组),二维数组

     a. 生成newCount:总的list个数
     b. 根据newCount创建一个新的Array
     c. array()->lists[addedCount] = oldList;:将oldList放到末尾
     d.memcpy:将新的lists放到首位memcpy(参数1:从什么位置,参数2:插入什么,参数3:插入的长度)
    

    总结

    分类的加载流程:
    realizeClassWithoutSwift -> methodizeClass -> attachCategories -> attachLists

主类与分类加载数据的时机

分类attachCategories执行时机

分析:attachCategories方法在什么时候、什么地点调用的呢
  • 全局搜索 attachCategories方法,发现只有在两个地方调用了

  • attachToClass方法中调用

在这里插入图片描述

  • load_categories_nolock方法中调用

在这里插入图片描述

  • 在这两个方法中打上断点就会发现是由load_categories_nolock -> 执行到的attachCategories方法中。

  • 在attachCategories方法处打断点,并在控制台bt输出堆栈信息
    在这里插入图片描述

  • 那么在bt堆栈信息中我们看到 load_images -> loadAllCategories -> load_categories_nolock -> load_categories_nolock -> attachCategories

在这里插入图片描述

懒加载类/非懒加载类 和 懒加载分类/非懒加载分类

在上述分析中,主类和分类都实现了load方法,即都是非懒加载,那么如果有两个分类,其中主类是非懒加载类,分类有一个是懒加载类一个是非懒加载类呢?
或者主类是懒加载,分类是非懒加载类;又或者主类和分类都为懒加载类呢?

  • 添加一个主类LGPerson,分类一:LGA,分类二:LGB

懒加载类与懒加载分类

  • 在realizeClassWithoutSwift方法出打一个断点,并在控制台输入bt打印一下堆栈,就会发现在消息第一次调用时,进行加载
    在这里插入图片描述

懒加载类与非懒加载分类

  • 在attachToClass处打个断点,查看ro的内容,就会发现methodlist中的个数是8个,说明类加载了

在这里插入图片描述

  • 继续向下执行就会发现执行attachCategories方法,开始加载分类

在这里插入图片描述

  • 主类懒加载,分类为非懒加载,那么就会迫使主类提前加载 。

非懒加载类与懒加载分类

非懒加载类与两个懒加载分类
  • LGPerson实现load方法,两个分类不实现load方法
  • 在realizeClassWithoutSwift方法中打个断点,查看ro发现methodList的个数有16(主类有两个属性和四个方法,每个分类都有四个方法)个,只在data()中读取,并只处理一下相同方法名的函数。
    在这里插入图片描述
非懒加载类与一个非懒加载分类和一个懒加载分类
  • LGA不实现Load方法,LGPerson和LGB添加load方法

  • 在attachCategories方法的 method_list_t *mlist = entry.cat->methodsForMeta(isMeta);处打一个断点查看entry.cat的内容

  • 第一次进入可以看到打印的name是LGA

在这里插入图片描述

  • 此处还在验证

主类和分类各种情况的总结 在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值