结合上一篇read_images,本篇继续分析类的加载流程。上一篇中,我们已经定位到了非懒加载类初始化的核心流程realizeClassWithoutSwift
,现在对realizeClassWithoutSwift
进行深入分析。
1.类初始化探索
在read_images
流程中,会对类进行一些修复工作,比如修复编译阶段selector
的混乱问题,修复一些消息,混乱类的处理等;同时会将类的名称与类进行关联,插入对照表中,并更新内存中的类表。
但是此时的类中还没有方法、协议、属性等内容,包括分类的一些扩展内容也没有插入到类中,那么编译生成的MachO文件
中类的相关信息,是何时插入到内存对应的cls
中的呢?见下图:
这是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.源码分析
-
首先进行相关变量的声明,判断类是否已经实现,如果已经实现直接返回。用于
递归控制
,因为后面的流程中会进行递归实现父类和元类,根类的父类是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); }
在此流程中,从
machO
中获取的数据地址,根据class_ro_t
格式进行强制,同时初始化rw
的空间,并复制一份ro
的数据放入rw
中。 -
递归处理,进行父类和元类的实现。
// 递归,加载父类、元类的实现 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
。
const char * className = "LGPerson";
if (strcmp(class_getName(cls), className) == 0)
{
printf("hello LGPerson...");
}
设置断点,分别判断在rw
初始化前,lldb
打印cls
的数据结构的变化。运行程序,打印cls
的内容,无法读取ro
数据。见下图:
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
,并将cls
的bits.data
设置为rw
。
lldb
验证一下结果如下:
lldb
打印出cls
的数据结构,从cls
中成功获取了ro
的数据。
- supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
- metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
继续运行程序会通过递归完成父类和元类和实现。本案例中,类LGPerson
继承自LGHuman
,其中LGPerson
为非懒加载
,即实现了+load
方法。见下图:
完成递归流程后,我们可以看到LGPerson类
在数据上的变化:
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
}
-
对方法、属性、协议进行处理。见下图:
但是这里发现个有意思的问题,
rwe为空
,很显然,此时还没有对类进行相关的扩展操作,所以rwe
还没有被创建初始化。此时针对方法、属性、协议的添加操作时无效的
! -
方法列表的处理中有些不同,调用了
prepareMethodLists
方法。那么该方法做了哪些操作呢?见下图:核心流程,
fixupMethodList
,根据注释:根据需要对selector
进行修复。进入fixupMethodList方法
,查看实现流程。见下图: -
继续
methodizeClass
源码的解读。找到了类初始化过程中非常关键的步骤,向类中添加分类方法、协议等,rwe
的初始化也在其中。
2.prepareMethodLists验证
上面在解读源码时发现prepareMethodLists
是对方法列表进行排序的过程,这里验证一下!在排序前,lldb
输出方法列表:
在经过prepareMethodLists
流程之后,lldb
输出方法列表:
方法的顺序发生了改变。输出sel
的地址,发现他们是按照线性递增排列的,这也就验证了,在慢速查找中,可以进行方法列表二分查找算法的原因。二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。见下图:
4.attachCategories探索
在methodizeClass
流程中,还有一行关键代码没有解析!将分类相关内容添加到类中。见下面代码:
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
继续运行代码,进入attachToClass
方法中,见下图:
很遗憾,没能进入到if
中,也就是没有走到attachCategories
方法!
- 正向探索没能成功,反向推导一下,那么
attachCategories
在哪些地方调用呢?
1.attachCategories反向推导
在libObjc.dylib
库中,全局所搜attachCategories
方法。见下图:
在load_categories_nolock
方法中调用了attachCategories
方法。那么load_categories_nolock
有在何处调用呢?再全局搜索load_categories_nolock
,见下图:
再全局搜索loadAllCategories
方法,最终发现在load_images
方法中调用了loadAllCategories
方法。见下图:
-
梳理一下流程:
- load_images
- loadAllCategories
- load_categories_nolock
- attachCategories
2.分类处理初探
创建一个LGPerson
的分类,并在分类中添加一个方法sayHello_cate
。见下面代码:
@implementation LGPerson (LG)
- (void)sayHello_cate{
NSLog(@"sayHello_cate %s", __func__);
}
@end
attachCategories
是向类中添加分类的内容,在load_categories_nolock
方法中添加过滤条件,过滤出我们所关心的LGPerson
类的分类处理流程!见下图:
运行代码,很遗憾,没有进入该断点。为什么?该方法是由load_images
调用的,那么与load方法
是否有关系呢?
修改分类LGPerson(LG)
,在其中添加load方法,如下:
@implementation LGPerson (LG)
+ (void)load{}
- (void)sayHello_cate{
NSLog(@"sayHello_cate %s", __func__);
}
@end
重新运行程序,奇迹发生了,成功过滤到LGPerson
,并走到attachCategories
流程中,并获取了LGPerson(LG)
分类中的方法。见下图:
到这里貌似一切谜底都解开了,但是别忘了类和分类中都实现了load方法
,也就是非懒加载的类和分类
处理流程谜底算是解开了。
那么懒加载的类和分类
是怎样的一个加载过程呢?这是我们要探索的问题!在下一篇文章中解密!!!
5.懒加载类的初始化流程分析
- 非懒加载类的初始化过程:
- map_images
- _read_images
- realizeClassWithoutSwift
那么懒加载类的初始化过程是怎样的呢?在methodizeClass
中设置过滤条件,过滤出我们所关心的LGPerosn类
,同时去掉LGPerosn
类和分类中的load
方法,运行代码:
我们发现在类进行第一次消息发送时,会对类进行初始化。
- 懒加载类的初始化流程为:
- lookUpImpOrForward
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
6.补充rw ro rwe深入分析
rw
和ro
已经比较理解了!
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
中的数据优先attach
到rwe数据结构
中。在读取数据时,会优先返回rwe
中的数据,如果rwe
没有初始化,则返回ro
中的数据。
底层定义了一个模板,利用模板机制可以显著减少冗余信息,能大幅度地节约程序代码,进一步提高面向对象程序的可重用性和维护性。