iOS底层原理之类的加载原理(中)

本文详细剖析了iOS中非懒加载类realizeClassWithoutSwift的执行流程,涉及类的初始化、父类和元类的递归实现、方法列表的排序与分类处理,以及lazy加载与非lazy加载的区别。

前言

在上一篇文章iOS底层原理之类的加载原理(上)中我们探索了_objc_init的流程,在_read_images里面知道了非懒加载类初始化方法realizeClassWithoutSwift,下面让我们一起来探索一下:

一、realizeClassWithoutSwift

自己加的这个方法是为了判断当前执行的类是否为要调试的LGPerson,如果不是,直接跳过,如果是再向下执行,

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

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    // 自己加的代码
    const char *mangledName = cls->nonlazyMangledName();
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        // 普通写得类 他是如何
        printf("%s - 要研究的: - %s\n",__func__,mangledName);
    }

    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?
    //    data() - ro -> 开辟rw
    //    从mach-o文件中读取数据data,转成class_ro_t的数据结构
    //    ro是在编译阶段即确定下来的数据结构 clean memory,而rw是运行时的结构 dirty memory ,所以需要开辟rw的数据空间

    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. ro -> rw
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }
    
    //    干净内存地址ro  脏内存地址rw
    //    运行时 - 内存  ro - copy - rw  内存操作比较严重
    //    并不是每一个类都需要插入,进行修改的很少,(rw扩展会导致内存占用很多,直接复制ro),如果动态性扩展一个rwe
    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#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->getSuperclass()), 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->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 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
    cls->setSuperclass(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
    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;
}

1.流程分析

首先进行相关变量的声明,判断类是否已经实现,如果已经实现直接返回。用于递归控制,因为后面的流程中会进行递归实现父类和元类,根类的父类是nil,元类的isa指向自己,所以这样可以保证类只会被初始化一次。

    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
  • rw初始化。这里涉及到干净内存clean memory和脏内存dirty memory的概念。

  • ro属于clean memory,在编辑时即确定的内存空间,只读,加载后不会发生改变的内存空间,包括类名称、方法、协议和实例变量的信息;
    rw的数据空间属于dirty memory,rw是运行时的结构,可读可写,由于其动态性,可以往类中添加属性、方法、协议。在运行时会发生变更的内存。

  • rwe类的额外信息。在WWDC2020中也提到,只有不到10%的类真正的更改了他们的方法,并不是每一个类都需要插入数据,进行修改的类很少,避免资源的消耗,所以就有了rwe。

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<class_rw_t>();
    rw->set_ro(ro);
    rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
    cls->setData(rw);
}
  • 实现父类和元类,如果还没有实现的情况下。
    这需要在上面设置 RW_REALIZED 之后完成,对于根类。
    这需要在选择类索引后完成,对于根元类。
    这假设这些类都没有 Swift 内容,或者 Swift 的初始化程序已经被调用。
    修正如果我们添加对 Swift 类的 ObjC 子类的支持,假设将是错误的。。
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
  • Isa处理,在前面学习isa的时候,对于NONPOINTER_ISA进行了位域处理,指针优化,Isa的末尾位是1,Isa不单单代表一个指针。而对于元类以及特殊情况下的场景的一些类,无需开启指针优化的类,使用Raw Isa,Isa的末尾位是0。
#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->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 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
  • 建立子类与父类的双定链表关系,保证子类能找到父类,父类也可以找到子类。初始化类的实例对象的大小,是否有c++析构函数设置,以及关联对象的相关设置。
// Update superclass and metaclass in case of remapping
    //    父类、isa关系
    cls->setSuperclass(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);

    //    c++函数
    // 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);
    }
  • cls->setInstanceSize(ro->instanceSize);
    这个地方是不是很熟悉,我们在创建对象的时候,class_createInstanceFromZone流程中,在进行对象大小初始化时,使用到了instanceSize。
  • 方法化当前的类,向类中添加方法,协议、属性,同时对方法列表进行排序等操作。
// Attach categories 附加类别 - 方法化当前的类,对类进行扩展
// Attach categories
methodizeClass(cls, previously);

2.流程总结

  • 当前cls对象为LGPerson,通过_read_images读取到当前的LGPerson,然后调用realizeClassWithoutSwift设置LGPerson的rw数据
  • 当前cls对象为LGPerson,通过realizeClassWithoutSwift方法内的supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);即在此调用此方法设置LGPerson的父类NSObject,但是此时父类NSObject是已经实例话的类即cls->isRealized判断为true,直接return cls,不会再次触发superclass与metaclass
  • 当前cls对象为LGPerson,通过realizeClassWithoutSwift方法内的metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);即在此调用此方法设置LGPerson的元类
  • 当前cls对象为LGPerson(metaclass),通过realizeClassWithoutSwift方法内的supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);即在此调用此方法设置LGPerson的元类的父类,也就是设置NSObject的元类,即根元类,但是此时是已经实例话的类即cls->isRealized判断为true,直接return cls,不会再次触发superclass与metaclass
  • 当前cls对象为LGPerson(metaclass),metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);即在此调用此方法设置LGPerson的元类的根元类,但是此时是已经实例话的类即cls->isRealized判断为true,直接return cls,不会再次触发superclass与metaclass,自此,isa指向关系完成。

二、methodizeClass

  • methodizeClass
  • 修复cls的方法列表,协议列表和属性列表。
  • 附加任何突出类别。
  • 锁定:runtimeLock必须由调用者持有

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->nonlazyMangledName();
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        // 普通写得类 他是如何
        if (!isMeta) {
            printf("%s -  要研究的: - %s\n",__func__,mangledName);
        }
    }

    // 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), 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);
    }

    // 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
}

1.流程分析

  • 将方法列表添加到数组中。
    重新分配未固定的方法列表。
    新方法预先添加到方法列表数组中。
static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle, const char *why)
{
    runtimeLock.assertLocked();

    if (addedCount == 0) return;
    
    const char *mangledName = cls->nonlazyMangledName();
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        printf("111111");
    }
    // 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());
    } else if (cls->cache.isConstantOptimizedCache()) {
        cls->setDisallowPreoptCachesRecursively(why);
    } else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
        SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
        SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
        if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
            cls->setDisallowPreoptInlinedSelsRecursively(why);
        }
#endif
    }

    // 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);
    }
}
  • 重新排序方法列表
static void 
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
    runtimeLock.assertLocked();
    ASSERT(!mlist->isFixedUp());

    if (!mlist->isUniqued()) {
        mutex_locker_t lock(selLock);
    
        for (auto& meth : *mlist) {
            const char *name = sel_cname(meth.name());
            meth.setName(sel_registerNameNoLock(name, bundleCopy));
        }
    }

    /// 按选择器地址排序。
    // 不要尝试对小列表进行排序,因为它们是不可变的。
    // 不要尝试对非标准大小的大列表进行排序,如 stable_sort
    // 不会正确复制条目。
    if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
    }
    
    // 将方法列表标记为唯一且已排序。
    // 不能标记小列表,因为它们是不可变的。
    if (!mlist->isSmallList()) {
        mlist->setFixedUp();
    }
}

2.验证及结论

根据前后不同的打印 和地址顺序 ,证明排序成功

上面 : cate_instanceMethod1 - 0x100003d4f
上面 : cate_instanceMethod2 - 0x100003d64
上面 : saySomething - 0x100003e05
上面 : say1 - 0x100003e12
上面 : say2 - 0x100003e17
上面 : say3 - 0x100003e1c
上面 : say4 - 0x100003e21
上面 : say5 - 0x100003e26
上面 : say6 - 0x100003e2b
上面 : say7 - 0x100003e30
上面 : lgName - 0x100003e35
上面 : setLgName: - 0x100003e3c
上面 : nickName - 0x100003e47
上面 : setNickName: - 0x100003e50
下面 : cate_instanceMethod1 - 0x100003d4f
下面 : cate_instanceMethod2 - 0x100003d64
下面 : saySomething - 0x100003e05
下面 : say1 - 0x100003e12
下面 : say2 - 0x100003e17
下面 : say3 - 0x100003e1c
下面 : say4 - 0x100003e21
下面 : say5 - 0x100003e26
下面 : say6 - 0x100003e2b
下面 : say7 - 0x100003e30
下面 : lgName - 0x100003e35
下面 : setLgName: - 0x100003e3c
下面 : nickName - 0x7fff7bf64d49
下面 : setNickName: - 0x7fff7bf64d52

结论:

  • 方法的顺序发生了改变。输出sel的地址,发现他们是按照线性递增排列的,这也就验证了,在慢速查找中,可以进行方法列表二分查找算法的原因。二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

三、懒加载类与非懒加载类

1.懒加载类

数据加载推迟到第一次发送消息

lookUpImpForward ->realizeClassMaybeSwiftMaybeRelock->realizeClassWithoutSwift->methodizeClass

2.非懒加载类

map_images的时候加载所有数据
_getObjc2NonlazyClassList ->realizeClassMaybeSwiftMaybeRelock->realizeClassWithoutSwift->methodizeClass

三、分类的本质探索

在上面我们提到了methodizeClass是对分类的操作,现在让我们去看看里面做了什么,首先创建LGPerson的分类

@interface LGPerson (LG) <NSObject>
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;

- (void)cate_instanceMethod1;
- (void)cate_instanceMethod2;
+ (void)cate_classMethod3;

@end

@implementation LGPerson (LG)

- (void)cate_instanceMethod1{
    NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
    NSLog(@"%s",__func__);
}
+ (void)cate_classMethod3{
    NSLog(@"%s",__func__);
}
@end

将main.m转化成c++文件,发现了这个,LGPerson的分类

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
	&_OBJC_$_CATEGORY_LGPerson_$_LG,
};

看一下这个_category_t


struct category_t {
    const char *name;
    classref_t cls;
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else return protocols;
    }
};

有点疑惑,回头看一下methodizeClass,发现里面有这样一段代码

    // 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);
        }
    }

进入到attachToClass

class UnattachedCategories : public ExplicitInitDenseMap<Class, category_list>
{
public:
    void addForClass(locstamped_category_t lc, Class cls)
    {
        runtimeLock.assertLocked();

        if (slowpath(PrintConnecting)) {
            _objc_inform("CLASS: found category %c%s(%s)",
                         cls->isMetaClassMaybeUnrealized() ? '+' : '-',
                         cls->nameForLogging(), lc.cat->name);
        }

        auto result = get().try_emplace(cls, lc);
        if (!result.second) {
            result.first->second.append(lc);
        }
    }

    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()) {
            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);
        }
    }

    void eraseCategoryForClass(category_t *cat, Class cls)
    {
        runtimeLock.assertLocked();

        auto &map = get();
        auto it = map.find(cls);
        if (it != map.end()) {
            category_list &list = it->second;
            list.erase(cat);
            if (list.count() == 0) {
                map.erase(it);
            }
        }
    }

    void eraseClass(Class cls)
    {
        runtimeLock.assertLocked();

        get().erase(cls);
    }
};

可以看出这里做了一些方法的处理,那么它又是怎么控制的呢,我们继分析,
我们先看下rwe,auto methodizeClass这个函数由这行代码rwe = rw->ext();为rwe控制,我们看下:

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    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 *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

从上图可以看出extAllocIfNeeded可以条件判断,我们搜下
extAllocIfNeeded,得到如下:
attachCategories在这个函数中找到了调用,在添加分类的时候rwe就是能够赋上值的。
addMethods_finish添加方法的时候也会赋值。
class_addProtocol添加协议的时候也会赋值。
_class_addProperty添加属性的时候也会赋值。
objc_duplicateClass重命名的时候也会赋值。
也就是说动态处理时候才会对rwe的处理。
我们重点关注attachCategories分类的加载。
我们搜索attachCategories

attachToClass有调用。
load_categories_nolock有调用。

总结

本次我们介绍了realizeClassWithoutSwift、methodizeClass、分类的本质探索、简单提到了懒加载类与非懒加载类,先告一段落,将在下一篇文章中讲述非懒加载类和非懒加载分类、attachCategories的详解,谢谢

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值