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

·  阅读 395

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

1.类初始化探索

read_images流程中,会对类进行一些修复工作,比如修复编译阶段selector的混乱问题,修复一些消息,混乱类的处理等;同时会将类的名称与类进行关联,插入对照表中,并更新内存中的类表。

但是此时的类中还没有方法、协议、属性等内容,包括分类的一些扩展内容也没有插入到类中,那么编译生成的MachO文件中类的相关信息,是何时插入到内存对应的cls中的呢?见下图:

image.png

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

2.realizeClassWithoutSwift分析

老方法,过滤出我们需要研究的类。在该部分添加过滤条件,设置断点过滤出LGPerson。我们在LGPerson中实现load方法,程序成功走入realizeClassWithoutSwift流程。源码如下:

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    const char *mangledName = cls->nonlazyMangledName();
    if (strcmp(mangledName, "LGPerson") == 0)
    {
        printf("LGPerson....");
    }
    
    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?
    //    data() - ro -> 开辟rw
    //    从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>();
        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);
    
    //    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);
    }

    // Attach categories 附加类别 - 方法化当前的类,对类进行扩展
    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}
复制代码

1.源码分析

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

        if (!cls) return nil;
        if (cls->isRealized()) {
            validateAlreadyRealizedClass(cls);
            return cls;
        }
    复制代码
  2. 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中。

  3. 递归处理,进行父类和元类的实现。

        // 递归,加载父类、元类的实现
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    复制代码
  4. 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
    复制代码
  5. 建立子类与父类的双定链表关系,保证子类能找到父类,父类也可以找到子类。初始化类的实例对象的大小是否有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

  6. 方法化当前的类,向类中添加方法,协议、属性,同时对方法列表进行排序等操作。

    // Attach categories 附加类别 - 方法化当前的类,对类进行扩展
    // Attach categories
    methodizeClass(cls, previously);
    复制代码

2.跟踪cls数据变化

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

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

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

image.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验证一下结果如下:

image.png

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

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

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

image.png

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

image.png

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

    image.png

    但是这里发现个有意思的问题,rwe为空,很显然,此时还没有对类进行相关的扩展操作,所以rwe还没有被创建初始化。此时针对方法、属性、协议的添加操作时无效的

  2. 方法列表的处理中有些不同,调用了prepareMethodLists方法。那么该方法做了哪些操作呢?见下图:

    image.png

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

    image.png

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

    image.png

2.prepareMethodLists验证

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

image.png

在经过prepareMethodLists流程之后,lldb输出方法列表:

image.png

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

image.png

4.attachCategories探索

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

objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
复制代码

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

image.png

很遗憾,没能进入到if中,也就是没有走到attachCategories方法!

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

1.attachCategories反向推导

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

image.png

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

image.png

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

image.png

  • 梳理一下流程:

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

2.分类处理初探

创建一个LGPerson的分类,并在分类中添加一个方法sayHello_cate。见下面代码:

    @implementation LGPerson (LG)
    - (void)sayHello_cate{
        NSLog(@"sayHello_cate %s", __func__);
    }
    @end
复制代码

attachCategories是向类中添加分类的内容,在load_categories_nolock方法中添加过滤条件,过滤出我们所关心的LGPerson类的分类处理流程!见下图:

image.png

运行代码,很遗憾,没有进入该断点。为什么?该方法是由load_images调用的,那么与load方法是否有关系呢?

修改分类LGPerson(LG),在其中添加load方法,如下:

    @implementation LGPerson (LG)
    + (void)load{}
    
    - (void)sayHello_cate{
        NSLog(@"sayHello_cate %s", __func__);
    }
    @end
复制代码

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

image.png

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

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

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

  • 非懒加载类的初始化过程:
    1. map_images
    2. _read_images
    3. realizeClassWithoutSwift

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

image.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中的数据。 底层定义了一个模板,利用模板机制可以显著减少冗余信息,能大幅度地节约程序代码,进一步提高面向对象程序的可重用性和维护性。

分类:
iOS
标签:
分类:
iOS
标签: