前言
通过篇文章--类的加载 上分析,知道我们的代码通过编译会生成machO可执行文件,然后再通过dyld链接进入内存里面来。是如何链接进入内存里面的了?我们通过read_images底层实现,分析了其中的重要流程。最后,我们定位到了非懒加载类初始化的核心流程realizeClassWithoutSwift,接下来就是对realizeClassWithoutSwift进行深入分析。
资源准备
objc源码下载:多个版本的objc源码- 苹果WWDC2020视频
- 小板凳、冰🍺
类的初始化
在上篇文章中,重点对核心read_images进行了分析,如创建类的总表,修复预编译阶段@selector 的混乱问题,关联类的信息,修复重映射一些没有被镜像文件加载进来的类,修复一些消息,修复没有被加载的协议,对分类的处理,还有非懒加载等等。
我们需要知道编译时生成machO可执行文件时如何存入内存中的,最后我们锁定到内的初始化 realizeClassWithoutSwift,回到源码中read_images里面,如下图:
通过这个源码,我们可以知道,对于非懒加载类(就是实现+ 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?
//✅ 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. ro -> rw
//✅ 复制ro的内容到rw里面
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);//✅ 再把rw存到类里面,所以在绝大多数地方,ro和rw是相似的,除了极个别被动态处理的(动态调整原始位置,比如说:分类)
}
//✅ 当原始内存开辟之后,一般是不再会变化的。就像在分类里面添加方法,并不会对ro进行处理
//✅ 真实内存地址--ro;脏内存地址--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.
//✅ 递归,加载父类、元类的实现(类的继承链图和isa走位图)
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.
//✅ 类的isa指针地址 和 元类的isa地址,是一样的
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;
}
- 通过
ro生成rw数据。 - 关联元类与父类。这里会递归父类和元类的
realizeClassWithoutSwift,最后与cls关联 isa的判断处理。- 调整
ivars的offset。 - 同步
flags的标志位给rw(实际上是从ro中存储到缓存中)。 - 关联子类与相邻的类。
- 分类的处理逻辑在
methodizeClass中。
源码细节分析
①、先是相关变量的声明,判断类是否已经实现,如果已经实现,就直接对rw进行赋值处理,然后直接返回。
因为在后面的流程中,会进行
递归实现父类和元类,根类的父类是nil,根元类的isa指向根类,所以这样可以保证类只会被初始化一次。
②、生成rw数据。在此流程中,从machO中获取的数据地址,根据class_ro_t格式进行强制,同时初始化rw的空间,并复制一份ro的数据放入rw中:
-
首先把
cls->data()赋给ro,在之前的class结构探索的时候data()获取的是rw数据。这是因为rw还没有赋值,从macho中读取__objc_classlist就存在了data()中了。具体可以在赋值前后验证cls->data()的地址。 -
先给
rw开辟空间,再将ro链接到rw的ro()中。(这里只是将地址给了rw,并没有完全复制一份,苹果对这块做了对应的优化。这里可以参考资源里面的WWDC2020视频。 -
设置
rw的flags(uint32_t类型),31位表示rw是否已经初始化完毕(RW_REALIZED==1<<31),19位表示rw是否初始化中(RW_REALIZING==1<<19),0位表示是否是元类(元类为1,非元类为0)。 -
最后将
rw存进cls的data中。
这里涉及到clean memory和dirty memory的概念。
-
ro属于clean memory,当在编译后,就确定了内存空间,基本不会变动,只读。加载后不会发生改变的内存空间,其中包含了类名称、方法、协议和实例变量等相关信息; -
rw属于dirty memory,rw是运行时的结构,内存空间可以变动,可读可写,由于其动态性,可以在运行时,向类里面动态添加属性、方法、协议。所以在运行时会发生变更的内存空间大小。 -
rwe属于类的额外信息。在rw中,只有不到10%的类真正的更改了他们的方法,并不是每一个类都需要插入数据,进行修改的类很少,避免资源的消耗,所以就有了rwe。 -
注1:这里有个细节是调试代码中判断了isMeta,因为元类和类的名称相同。否则元类也会进入这块逻辑。 -
注2在源码里面,有auto ro = (const class_ro_t *)cls->data();,为什么ro是由cls->data()进行赋值了? 我们看一下data()的实现,它是一个class_rw_t类型,而class_rw_t里面,就包含了类的众多信息,如:protocols、properties、methods、deepCopy、extAllocIfNeeded、ext、flags、witness、index。。。等等。
class_rw_t *data() const {
return bits.data();
}
cls->data()读取出来是一个地址指针,而这个指针根据指定的格式 class_ro_t,找到其对应的类型class_rw_t,拿到这个类型后,就能对class_rw_t里面的内容挨个进行赋值。除非当前的这个格式,得到的地址无法解析到位,就好比通过这个地址所得到的内容,和原始编排的格式不匹配,所以就会解析不出来。
所以,ro 一旦从 cls->data() 也就是 machO 里面读到地址指针,就能根据其相对格式class_ro_t,对class_rw_t里面的内容进行一一赋值。
那么接着往下走。
通过lldb调试跟踪下,
根据打印结果,LGPerson 里面定义的方法都没有被查出来,只能获取LGPerson类的地址,但是其里面的所有信息都没有。还没被调用。sel和imp还没有形成一个绑定关系;
接着就是 rw 开辟空间,ro 赋值给 rw,再把 rw 赋值给 cls 的 data。这里是把干净内存 ro 复制一块到 rw 里。这也是很多地方看到 rw 和 ro 数据是相似的,除非一些动态化处理的地方(比如说 addmethod,addproperty,category),这也是为什么 ro 在最原始的时候开辟完之后就不会再变了。因为不会直接对 ro 进行处理。
③、cls关联元类与父类,通过递归处理,进行父类和元类的实现,如下图:
与下面的cls->setSuperclass(supercls);和cls->initClassIsa(metacls);相呼应。
④、isa的判断处理:
判断设置
isa是否纯指针。
- 元类
isa是纯指针。 - 类的
isa是否纯指针取值flags(FAST_CACHE_REQUIRES_RAW_ISA1<<13)第13位。 DisableNonpointerIsa也就是OBJC_DISABLE_NONPOINTER_ISA环境变量配置来配置是否纯指针。- 父类是纯指针,并且父类还有父类。那么自己也要是纯指针。
rawIsaIsInherited(只是控制打印)表示继承的是纯指针。 - 递归设置
isa为纯指针,子类也设置为纯指针。(父类为纯指针,子类也为纯指针)。
⑤、关联父类与元类,也就是继承链与isa走位
⑥、调整 ivar 的 offset
- 在有父类并且非元类的情况下,会进行
ivar offset的调整,在reconcileInstanceVariables实现。 - 重新设置成员变量的大小。在
setInstanceSize中实现,其中有对常量的修改。类实例对象的大小设置-在对象初始化的时候会用到
⑦、rw 同步 ro 标志位
- 拷贝
ro的flags到rw中(其实是放在缓存中)。 RO_HAS_CXX_STRUCTORS (1<<2)flags第2位标记c++构造方法。RO_HAS_CXX_DTOR_ONLY (1<<8)flags第8位标记c++析构方法。- 本类禁止关联对象或者父类禁止关联对象,则同步
RW_FORBIDS_ASSOCIATED_OBJECTS (1<<20)标记。flags第20位标记是否禁止关联对象。
⑧、建立父类、子类的双向链表关系
-
addSubclass----关联父类的子类。同时也关联子类的相邻类以及c++构造和析构的标记。同时根据父类是否isa纯指针同步给子类。 -
addRootClass关联根类,在这个流程中NSObject的相邻类会被设置为nil,_firstRealizedClass会被设置为NSObject。 -
addSubclass详情 -
addRootClass详情
⑨、方法化当前的类,向类中添加方法,协议、属性,同时对方法列表进行排序等操作:
然后当执行到 methodizeClass 函数之前,打印 cls 的地址,还是打印不出来,但是我们在前面的分析中,可以知道ro和rw是进行了赋值操作的,然而在这里却打印不出来,这说明了在methodizeClass之前,并没有对 ro 和 rw 进行处理,打印结果如下图:
那么接下来,我们就要对methodizeClass函数进行详细分析了。
methodizeClass分析
先看下源码:
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.
//✅ 获取ro方法列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
//✅ 获取ro属性列表
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.
//✅ 是否根元类,根元类加了initialize方法。
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 的值是为 NULL 的。
- 对方法的
attachLists处理。(当rwe为空时,不执行) - 对属性的
attachLists处理。(当rwe为空时,不执行) - 对协议的
attachLists处理。(当rwe为空时,不执行) - 对根元类添加
initialize方法。 - 对分类的
attachToClass处理。(最终不会走进attachCategories逻辑)
prepareMethodLists 分析
通过断点调试发现,刚开始时,虽然能获取方法列表 list 的地址,但是并不能拿到里面的方法,如下图:
也就是说,方法列表并没有被处理,放入ro里面,而是要通过prepareMethodLists函数进行处理,查看该函数源码:
fixupMethodList分析
核心流程,fixupMethodList,根据注释:根据需要对selector进行修复。进入fixupMethodList方法,查看实现流程。见下图:
-
先对
method的name也就是SEL进行了修正。sel_registerNameNoLock -> __sel_registerName -> search_builtins->_dyld_get_objc_selector。相当于以dyld的为准。 -
接着对
methodList进行了排序(按照SEL的地址),这里就与慢速消息查找的二分查找对应上了。排序是在这里进行的。 -
最后设置标志位。
-
注: 根据注释// Don't try to sort small lists, as they're immutable,说明small类型的,不可变。
打印排序情况,如下图:
-
方法的顺序默认是按照编译时候的顺序排序的,一般情况下是有序的。(文件中方法的顺序)
-
在进行
dyld修正SEL地址后需要重新排序。
在排序前,方法地址大小有点错乱,当排序后,方法地址大小就从小到大排序。我们在前面的博客里面分析方法慢速查找的时候,里面就有二分法查找,里面就有排序。
分类的本质
当prepareMethodLists执行完成后,因为rwe为空,所以后续的attachLists不会执行。根据之前WWDC的介绍rwe在有分类的情况下会出现,那么就加个分类,如下图:
通过 clang 命令,还原成C++代码,打开main.cpp文件,可以知道分类在底层的实现,如下图:
_CATEGORY_LGPerson_$_LG 是 _category_t 类型:
根据 main.cpp 文件,可以知道:
- 分类也是一个结构体类型;
name就是分类的名字,就是LG;cls就是LGPerson类;- 在类中只有一个
methods,在分类中有了instance_methods与class_methods。因为分类是没有元类的(也就是没有分元类)。 - 分类中是有
properties的。
再看下赋值情况,此时还只是静态编译阶段,没有到达运行时,所以还没有真正的赋值,现在只是占位的随机值,但是也能看出一二来,如下图:
遵循的协议:
这里是NSObject协议。
属性赋值:
但是并没有属性所对应的 set 和 get 方法。
方法列表:
所以只能通过关联对象处理。
看了分类的 category_t 在 c++ 文件里面的实现,那么在objc源码中,是否有对应的代码段了?那么直接到源码中去查找。
category_t 源码分析
看源码:
- 在源码中
category_t与clang转换的c++代码里面的category_t相比,两者之间的结构有所不同。 - 源码中的
category_t支持类属性----_classProperties。但是在c++文件里面却没有。
category在 Xcode 文档探索
通过快捷键 command + shift + 0 进入到文档里面(先打开Xcode,再使用快捷件),在文档里面搜索 Category,就能找到对应的位置,如下图:
在文档中,发现category是 objc_category 类型的,那么可以在objc源码中,找找:
我们分析了分类的本质,知道分类的构成情况,在分类里面,无论是方法、属性、协议等,都在分类的结构体里面,那么是怎么被类所调用的了?
接下来继续回到objc源码中我们所分析的methodizeClass函数里面。
分类的加载
通过类的加载源码分析,方法、属性、协议等通过attachLists来实现,分类通过attachToClass来实现。他们的控制条件是rwe。而rwe来源于rw->ext():
进入到 ext,查看实现:
- 当存在时,直接去
rwe, 当不存在的话,就直接创建。
那么就可以在源码中,查看下 extAllocIfNeeded 的调用情况,就知道使用 extAllocIfNeeded 的限制条件了。下面就是在源码中的调用情况:
- 在
attachCategories中调用。 - 在
demangledName的判断isRealized()或者isFuture()进行取值。 - 在
class_setVersion类的版本设置,调用了。 - 在
addMethods_finish调用。 - 在
class_addProtocol调用。 - 在
_class_addProperty调用。 - 在
objc_duplicateClass调用。
也就是在动态运行时,再有 rwe 的动态创建和处理。
那么分类是什么时候加载的了?
attachCategories 初探索分析
在 methodizeClass 中,根据源码的注释和我们自己的分析,有一个初步的探索路线,由 methodizeClass 函数:
再到 attachToClass 里面的实现:
最后到 attachCategories的具体实现:
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
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, __func__);
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, __func__);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) {
flushCaches(cls, __func__, [](Class c){
// constant caches have been dealt with in prepareMethodLists
// if the class still is constant here, it's fine to keep
return !c->cache.isConstantOptimizedCache();
});
}
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
结合上文的分析,可以看到除了attachCategories,其它的代码块逻辑,要么是对类进行动态处理,要么是修复类的时候创建rwe。那么显然核心逻辑就在attachCategories了。
而对attachCategories有调用操作的,就是attachToClass(刚刚上面图片中就有)与load_categories_nolock,如下图:
attachToClass是在methodizeClass中调用的- 通过全文索引
load_categories_nolock,知道其是在_read_images(这里不会调用)与loadAllCategories中调用。 - 而
loadAllCategories还是在load_images的时候加载。
所以分类的加载就有了两条线路:
methodizeClass -> attachToClass -> attachCategoriesload_images -> loadAllCategories -> load_categories_nolock -> attachCategories
那么分类具体加载的详情是怎样的了?将在下篇文章分析。
总结
realizeClassWithoutSwift- 通过
ro给rw赋值,生成rw数据。这里rw只是关联到了ro。 isa判断处理,关联元类与父类。- 元类
isa是纯指针。 isa是递归设置的,父类为纯指针,子类也为纯指针。
- 元类
- 调整
ivars的offset - 同步
flags的标志位给rw(实际上是从ro中存储到缓存中)。 - 关联子类(
firstSubclass)与相邻的类(nextSiblingClass)。 methodizeClass(主要是对方法列表进行排序 & 加载分类 & rwe 的处理)prepareMethodListsfixupMethodList修正并且排序方法列表(ro的)sel_registerNameNoLock最终执行_dyld_get_objc_selector将SEL地址修复为dyld提供的。SortBySELAddress对方法列表进行排序。
attachToClass分类的加载。
- 通过
rwerwe是在extAllocIfNeeded中创建的。- 加载分类。
- 动态修改类(
addMethods_finish、class_addProtocol、_class_addProperty)。
- 修复类(
demangledName、class_setVersion、objc_duplicateClass)。
- 分类的加载有两条路径:
methodizeClass -> attachToClass -> attachCategories。load_images -> loadAllCategories -> load_categories_nolock -> attachCategories。