iOS底层学习——类的加载分析

80 阅读15分钟

结合上一篇read_images本篇继续分析类的加载流程。上一篇中,我们已经定位到了非懒加载类初始化的核心流程realizeClassWithoutSwift,现在对realizeClassWithoutSwift进行深入分析。

1.类初始化探索

read_images流程中,会对类进行一些修复工作,比如修复编译阶段selector的混乱问题,修复一些消息,混乱类的处理等;同时会将类的名称与类进行关联,插入对照表中,并更新内存中的类表。 但是此时的类中还没有方法、协议、属性等内容,包括分类的一些扩展内容也没有插入到类中,那么编译生成的MachO文件中类的相关信息,是何时插入到内存对应的cls中的呢?见下图:

1.png 这是read_images中类初始化的关键流程,通过注释我们可以知道,针对非懒加载的类进行初始化操作,何为非懒加载的类呢,实现+load方法。通过nlclslist函数获取非懒加载类列表,对类进行递归处理,完成非懒加载类的初始化工作。

2.realizeClassWithoutSwift分析

老方法,过滤出我们需要研究的类。在该部分添加过滤条件,设置断点过滤出JhsPerson。我们在JhsPerson中实现load方法,程序成功走入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));

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

    //    从mach-o文件中读取数据data,转成class_ro_t的数据结构

    //    ro是在编辑阶段即确定下来的数据结构,而rw是运行时的结构,所以需要开辟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.

        rw = objc::zalloc<class_rw_t>();

        //    data() - ro -> 开辟rw

        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 附加类别 - 方法化当前的类,对类进行扩展

    // Attach categories

    methodizeClass(cls, previously);

    return cls;

}

1.源码分析

  1. 首先进行相关变量的声明,判断类是否已经实现,如果已经实现直接返回。用于递归控制,因为后面的流程中会进行递归实现父类和元类,根类的父类是nil元类的isa指向自己,所以这样可以保证类只会被初始化一次。
    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
  1. rw初始化。这里涉及到干净内存clean memory脏内存dirty memory的概念。
  • ro属于clean memory在编辑时即确定的内存空间,只读,加载后不会发生改变的内存空间,包括类名称、方法、协议和实例变量的信息;
  • rw的数据空间属于dirty memoryrw是运行时的结构,可读可写,由于其动态性,可以往类中添加属性、方法、协议。在运行时会发生变更的内存
  • 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);
}

在此流程中,从machO中获取的数据地址,根据class_ro_t格式进行强制,同时初始化rw的空间,并复制一份ro的数据放入rw中。

  1. 递归处理,进行父类和元类的实现。
    // 递归,加载父类、元类的实现
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
  1. Isa处理,在前面学习isa的时候,对于NONPOINTER_ISA进行了位域处理,指针优化,Isa的末尾位是1Isa不单单代表一个指针。而对于元类以及特殊情况下的场景的一些类,无需开启指针优化的类,使用Raw IsaIsa的末尾位是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

  1. 建立子类与父类的双定链表关系,保证子类能找到父类,父类也可以找到子类。初始化类的实例对象的大小是否有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);
    }

  1. 方法化当前的类,向类中添加方法,协议、属性,同时对方法列表进行排序等操作
// Attach categories 附加类别 - 方法化当前的类,对类进行扩展
// Attach categories
methodizeClass(cls, previously);

2.跟踪cls数据变化

同样的方法,我们在流程中添加一份关键代码,过滤出我们想要研究的类JhsPerson

    const char * className = "LGPerson";
    if (strcmp(class_getName(cls), className) == 0)
    {
        printf("hello LGPerson...");
    }

设置断点,分别判断在rw初始化前,lldb打印cls的数据结构的变化。运行程序,读取ro数据。见下图:

2.png

  • auto ro = (const class_ro_t *)cls->data();获取machO文件中的数据地址,按照class_ro_t格式进行强转。

  • auto isMeta = ro->flags & RO_META;判断是否为元类。

  • rw = objc::zalloc<class_rw_t>();初始化rw,将ro复制给rw,并将clsbits.data设置为rw

lldb打印出cls的数据结构,从cls中成功获取了ro的数据。

  • supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);

  • metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

继续运行程序会通过递归完成父类和元类和实现。本案例中,类JhsPerson继承自JhsTeacher,其中JHSPerson非懒加载,即实现了+load方法。见下图:

3.png

完成递归流程后,我们可以看到JHSPerson类在数据上的变化:

4.png

可以看到初始化前和初始化后及bits发生变化

3.methodizeClass分析

realizeClassWithoutSwift完成类的初始化后,rw也已经创建,并能够从cls中获取相关的属性、方法等。那么realizeClassWithoutSwift中最后一行代码methodizeClass方法做了什么呢?

1.源码分析

首先解读一下源码:

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)" : "");
    }

    // 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. 对方法、属性、协议进行处理。见下图:

5.png

但是这里发现个有意思的问题,rwe为空,很显然,此时还没有对类进行相关的扩展操作,所以rwe还没有被创建初始化。此时针对方法、属性、协议的添加操作时无效的! 2. 方法列表的处理中有些不同,调用了prepareMethodLists方法。那么该方法做了哪些操作呢?见下图:

6.png

核心流程,fixupMethodList,根据注释:根据需要对selector进行修复。进入fixupMethodList方法,查看实现流程。见下图:

7.png

继续methodizeClass源码的解读。找到了类初始化过程中非常关键的步骤,向类中添加分类方法、协议等,rwe的初始化也在其中。

8.png

2.prepareMethodLists验证

上面在解读源码时发现prepareMethodLists是对方法列表进行排序的过程,这里验证一下!在排序前,lldb输出方法列表:

9.png

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

10.png

4.attachCategories探索

methodizeClass流程中,还有一行关键代码没有解析!将分类相关内容添加到类中。见下面代码:

objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

继续运行代码,进入attachToClass方法中,见下图:

11.png

  • 正向探索没能成功,反向推导一下,那么attachCategories在哪些地方调用呢?

1.attachCategories反向推导

libObjc.dylib库中,全局所搜attachCategories方法。见下图:

12.png

load_categories_nolock方法中调用了attachCategories方法。那么load_categories_nolock有在何处调用呢?再全局搜索load_categories_nolock,见下图:

13.png

再全局搜索loadAllCategories方法,最终发现在load_images方法中调用了loadAllCategories方法。见下图:

14.png

  • 梳理一下流程:

    1. load_images
    2. loadAllCategories
    3. load_categories_nolock
    4. attachCategories

2.分类处理初探

创建一个JhsPerson的分类,并在分类中添加一个方法- (void)saySomething;。见下面代码:

- (void)saySomething{

    NSLog(@"%s",__func__);

}

15.png

运行代码,很遗憾,没有进入该断点。为什么?该方法是由load_images调用的,那么与load方法是否有关系呢? 修改分类JhsPerson(JhsA),在其中添加load方法,如下:

+ (void)load{}

- (void)saySomething{

    NSLog(@"%s",__func__);

}

重新运行程序,奇迹发生了,成功过滤到JhsPerson,并走到attachCategories流程中,并获取了JhsPerson(JhsA)分类中的方法。见下图:

到这里貌似一切谜底都解开了,但是别忘了类和分类中都实现了load方法,也就是非懒加载的类和分类处理流程谜底算是解开了。

那么懒加载的类和分类是怎样的一个加载过程呢?这是我们要探索的问题!在下一篇文章中解密!!

5.懒加载类的初始化流程分析

  • 非懒加载类的初始化过程:

    1. map_images
    2. _read_images
    3. realizeClassWithoutSwift 那么懒加载类的初始化过程是怎样的呢?在methodizeClass中设置过滤条件,过滤出我们所关心的JhsPerosn类,同时去掉JhsPerosn类和分类中的load方法,运行代码:

17.png 我们发现在类进行第一次消息发送时,会对类进行初始化。

  • 懒加载类的初始化流程为:

    1. lookUpImpOrForward
    2. realizeClassMaybeSwiftMaybeRelock
    3. realizeClassWithoutSwift

6.补充rw ro rwe深入分析

rwro已经比较理解了!

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

rwe是什么呢?与rw、ro有什么关系呢?内部到底是怎么存储的呢?  看源码:

//  rw
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
}

// ro
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}

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


内部实现的关键是:

using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;

底层定义了一个模板,利用模板机制可以显著减少冗余信息,能大幅度地节约程序代码,进一步提高面向对象程序的可重用性和维护性。 模板中提供了is数据判断get获取数据storeAt存储数据等方法。

template <class PT1, class PT2>
class PointerUnion {
    uintptr_t _value;

    static_assert(alignof(PT1) >= 2, "alignment requirement");
    static_assert(alignof(PT2) >= 2, "alignment requirement");

    struct IsPT1 {
      static const uintptr_t Num = 0;
    };
    struct IsPT2 {
      static const uintptr_t Num = 1;
    };
    template <typename T> struct UNION_DOESNT_CONTAIN_TYPE {};

    uintptr_t getPointer() const {
        return _value & ~1;
    }
    uintptr_t getTag() const {
        return _value & 1;
    }

public:
    explicit PointerUnion(const std::atomic<uintptr_t> &raw)
    : _value(raw.load(std::memory_order_relaxed))
    { }
    PointerUnion(PT1 t) : _value((uintptr_t)t) { }
    PointerUnion(PT2 t) : _value((uintptr_t)t | 1) { }

    void storeAt(std::atomic<uintptr_t> &raw, std::memory_order order) const {
        raw.store(_value, order);
    }

    template <typename T>
    bool is() const {
        using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
            PointerUnionTypeSelector<PT2, T, IsPT2,
            UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
        return getTag() == Ty::Num;
    }

    template <typename T> T get() const {
      ASSERT(is<T>() && "Invalid accessor called");
      return reinterpret_cast<T>(getPointer());
    }

    template <typename T> T dyn_cast() const {
      if (is<T>())
        return get<T>();
      return T();
    }
};

class_rw_t中,所提供的方法里都有这样的一段代码:

    auto v = get_ro_or_rwe();
    if (v.is<class_rw_ext_t *>()) {
        v.get<class_rw_ext_t *>()->...  // 省略
    } else {
        ...  // 省略
    }

    // get_ro_or_rwe实现
    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

可以理解为调用get_ro_or_rwe(),获取模板ro_or_rw_ext_t ;再调用模板的is<class_rw_ext_t *>()方法,判断是否存在rwe,即是否存在class_rw_ext_t数据空间;如果存在,则调用get<class_rw_ext_t *>()方法rwe的数据空间中获取对应的数据。 那么rwe什么时候被创建的呢?动态向本类中添加方法、协议、分类信息的时候,会调用extAllocIfNeeded方法来初始化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;
    
    // 初始化,会优先将ro中的baseMethodList放入class_rw_ext_t->methods,所以对于运行时rwe附加信息的方法列表,一定会存在全部的方法列表。
    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;
}

通过源码可以发现,在创建rwe,也就是创建class_rw_ext_t时,会将ro优添加到class_rw_ext_t数据结构中。 那么ro的数据哪来的呢?我们上面已经研究了!!!看设置ro的源码实现:

void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

 void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

 const class_ro_t *ro() const {
        auto v = get_ro_or_rwe(); // 获取模板
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_t *>();
    }

同样依然会调用get_ro_or_rwe(),获取模板ro_or_rw_ext_t ;再调用模板的is<class_rw_ext_t *>()方法,判断是否存在rwe,即是否存在class_rw_ext_t数据空间;如果不存在rwe,则存储在rw中,即拷贝一份到rw中。 而在获取ro数据时,如果rwe已经存在,则直接返回rwe中的ro,如果rwe不存在,直接返回rw中拷贝的ro

总结:

在类实现过程中,会初始化rw(class_rw_t),从可执行文件macho中读取类的ro(class_ro_t)数据,并将ro拷贝至rw。此时rwe并没有初始化。在运行时,需要动态向类中添加方法、协议,会创建rwe空间,并将ro中的数据优先attachrwe数据结构中。在读取数据时,会优先返回rwe中的数据,如果rwe没有初始化,则返回ro中的数据。 底层定义了一个模板,利用模板机制可以显著减少冗余信息,能大幅度地节约程序代码,进一步提高面向对象程序的可重用性和维护性。