接上篇继续分析
_read_images
方法, 上篇探讨了readClass
,只有了类的地址和名字,类的内部数据还并未实现,本章分析实现类方法realizeClassWithoutSwift
0x00 -- realizeClassWithoutSwift
分析
realizeClassWithoutSwift
部分关键代码, 包含了rw ro
的数据操作,这个初始化方法在之前的
方法的慢速查找流程
也有涉及,路径是
:
lookUpImpOrForward
realizeClassMaybeSwiftAndLeaveLocked
realizeClassMaybeSwiftMaybeRelock
realizeClassWithoutSwift
也就是说在发送消息的时候会去判断类是否实现, 如果类本身没有准备好,没有实现,没有ro, rw, rw
这个方法的主要作用是实现类
,将实现完的类数据放到内存中,程序去内存中读取数据,操作主要分为以下几步:
- 读取
Mach-O 的data
数据, 设置ro rw
- 递归调用
realizeClassWithoutSwift
实现继承链的完善 - 通过
methodizeClass
方法类
0x01 - 读取data数据
class_rw_t *rw;
Class supercls;
Class metacls;
// fixme verify class is not in an un-dlopened part of the shared cache?
//读取class的data(),以及ro/rw创建
auto ro = (const class_ro_t *)cls->data(); //读取类结构的bits属性、//ro -- clean memory,在编译时就已经确定了内存
auto isMeta = ro->flags & RO_META; //判断元类
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data(); //dirty memory 进行赋值
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>(); //申请开辟zalloc -- rw
rw->set_ro(ro);//rw中的ro设置为临时变量ro
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);//将cls的data赋值为rw形式
}
⚠️
cls
是从classref_t const *classlist = _getObjc2NonlazyClassList(hi, &count);
这个classlsit
列表获取的, 这个列表是从Mach-O
里拿到的, 所以cls->data()
拿到的数据也是Mach-O
的,
-
ro
表示read only
的意思, 即只读, 包含名称, 方法, 协议以及实例变量信息, 因为是只读的,所以也就是WWDC
说的clean memory
,而clean memory
在加载到内存后是不会发生改变的。 -
rw
表示read write
,就是可读可写
, 由于runtime
的动态性, 可能会给类中添加属性 、方法、协议等一些内容, 需要改变这个类的数据结构, 在新的WWDC
的Advancements in the Objective-C Runtime中,提到了rw
,其实在rw
中只有10%的类真正的更改了它们的方法,所以有了rwe
,即类的额外信息
。对于那些确实需要额外信息的类,可以分配rwe扩展记录中的一个,并将其滑入类中供其使用。其中rw
就属于dirty memory
,而dirty memory
是指在进程运行时会发生更改的内存
,类结构
一经使用
就会变成ditry memory
,因为运行时会向它写入新数据,例如 创建一个新的方法缓存,并从类中指向它在
addMethod
、addProperty
以及addProtocol
都会通过extAllocIfNeeded()
方法生成或者获取ext
的空间。
0x02 - 递归完善继承链
👇继续看realizeClassWithoutSwift
部分代码
// 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. --
//递归调用realizeClassWithoutSwift完善继承链,并处理当前类的父类、元类
//递归实现 设置当前类、父类、元类的 rw,主要目的是确定继承链 (类继承链、元类继承链)
//实现元类、父类
//当isa找到根元类之后,根元类的isa是指向自己的,不会返回nil从而导致死循环——remapClass中对类在表中进行查找的操作,如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
...
// Update superclass and metaclass in case of remapping -- class 是 双向链表结构 即父子关系都确认了
// 将父类和元类给我们的类 分别是isa和父类的对应值
cls->superclass = supercls;
cls->initClassIsa(metacls);
...
// Connect this class to its superclass's subclass lists
//双向链表指向关系 父类中可以找到子类 子类中也可以找到父类
//通过addSubclass把当前类放到父类的子类列表中去
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
递归调用realizeClassWithoutSwift
方法来完善父类
以及元类
,也设置了父类和元类的rw
,通过setInstancesRequireRawIsaRecursively
设置了父类和元类的isa
对应的值
通过addSubclass
和addRootClass
设置父子双向链表的关系,也就是 父类可以找到子类,子类中也可以找到父类
⚠️在realizeClassWithoutSwift
元类递归时, 由于根元类
指向本身, 所以不会返回nil
,所以有以下的终止条件
,保证每个类只初始化一次
,
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
- 如果类
不存在
,则返回nil
- 如果类
已经实现
,则直接返回cls
在remapClass
中,如果cls
不存在,也会直接返回nil
/***********************************************************************
* remapClass
* Returns the live class pointer for cls, which may be pointing to
* a class struct that has been reallocated.
* Returns nil if cls is ignored because of weak linking.
* Locking: runtimeLock must be read- or write-locked by the caller
**********************************************************************/
static Class remapClass(Class cls)
{
runtimeLock.assertLocked();
if (!cls) return nil;//如果cls不存在,则返回nil
auto *map = remappedClasses(NO);
if (!map)
return cls;
auto iterator = map->find(cls);
if (iterator == map->end())
return cls;
return std::get<1>(*iterator);
}
0x03 - methodizeClass
方法化类
⚠️⚠️⚠️ 想要从_read_images
走进realizeClassWithoutSwift
,需要在自定义的类Person
中实现+ load
方法,然后才能在realizeClassWithoutSwift中走调用methodizeClass
方法。因为懒加载类需要提前初始化,load
方法是在load_images
的时候调用,所以需要提前准备好。
- 懒加载类
懒加载类 , 第一次发送消息, 迫使这个类去实现它。,
- 非懒加载类
有load方法就是非懒加载类, 因为有load方法是在
load_images
的时候会调用,如果类没有实现,怎么调用load
方法
来到到realizeClassWithoutSwift
类的最底部,看到methodizeClass
方法化类的代码:
// 贴进类别 -- 疑问:ro中也有方法列表 rw中也有方法列表,下面这个方法可以说明
//将ro数据写入到rw
// Attach categories
methodizeClass(cls, previously);
return cls;
来到methodizeClass
方法,也可以通过LLDB
指令通过之前学习的内存偏移来调试cls
的内部数据。
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data(); // 初始化一个rw
auto ro = rw->ro();
auto rwe = rw->ext();
...
// Install methods and properties that the class implements itself.
//将属性列表、方法列表、协议列表等贴到rw中
// 将ro中的方法列表加入到rw中
method_list_t *list = ro->baseMethods();//获取ro的baseMethods
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));//methods进行排序
if (rwe) rwe->methods.attachLists(&list, 1);//对rwe进行处理
}
// 加入属性
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);
....
}
- 主要是将
方法列表
、属性列表
以及协议列表
放到rwe
中 - 通过
rw
获取ro
和rwe
- 通过
ro->baseMethods();
获取方法列表 - 通过
attachLists
插入对应的数据表
prepareMethodLists
方法内
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
...
// 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*/);//排序
}
}
...
}
在prepareMethodLists
方法内是对方法列表按sel
地址有排序,在lookUpImpForward
的流程里,查找方法列表是按二分查找的, 所以这里有对方法列表的排序。
Sort by selector address.
通过sel地址来排序。
attachToClass
分析
void attachToClass(Class cls, Class previously, int flags)
{
runtimeLock.assertLocked();
ASSERT((flags & ATTACH_CLASS) ||
(flags & ATTACH_METACLASS) ||
(flags & ATTACH_CLASS_AND_METACLASS));
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
bool kc_isMeta = cls->isMetaClass();
auto kc_rw = cls->data();
auto kc_ro = kc_rw->ro();
if (!kc_isMeta) {
printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
}
}
auto &map = get();
auto it = map.find(previously);//找到一个分类进来一次,即一个个加载分类,不要混乱
if (it != map.end()) {//这里会走进来:当主类没有实现load,分类开始加载,迫使主类加载,会走到if流程里面
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
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
在这里有一个iterator
迭代器 循环调用attachCategories
,找到一个调用一次
attachCategories
分析
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);
/*
rwe的创建,
那么为什么要在这里进行`rwe的初始化`?因为我们现在要做一件事:往`本类`中`添加属性、方法、协议`等
*/
auto rwe = cls->data()->extAllocIfNeeded();
//mlists 是一个二维数组
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) {//mcount = 0,ATTACH_BUFSIZ= 64,不会走到if里面的流程
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);//准备排序
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);//排序
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);//mlists + ATTACH_BUFSIZ - mcount 为内存平移
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
- 走到这里使用添加分类里,所以会通过
extAllocIfNeeded();
开辟rwe
的空间,即rwe
有值了, 对原来的clean memory
要进行处理了。 mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
拆分来写
mcount += 1
mlists[ATTACH_BUFSIZ - mcount] == mlists[64-1] // 也就是将数据从尾部开始插入
- 这里的
ATTACH_BUFSIZ=64
,即可以理解为倒序插入
,64
的原因是允许容纳64个(最多64个分类)
在
addMethod
、addProperty
以及addProtocol
都会通过extAllocIfNeeded()
方法生成或者获取ext
的空间。
// 781版本
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 *>());
}
}
// 818.2版本
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {//判断rwe是否存在
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);//如果存在,则直接获取
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));//如果不存在则进行开辟
}
}
👇//extAlloc源码实现
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
//此时只有rw,需要对rwe进行数据添加,即0-1的过程
auto rwe = objc::zalloc<class_rw_ext_t>();//创建
rwe->version = (ro->flags & RO_META) ? 7 : 0;
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;
}
- 在
extAllocIfNeeded
的内部,首先开辟内存, 接着把ro
的数据初始化给rwe
,也就是本类的数据加载到了rwe
.
总结: 修改本类数据clean memory
的地方需要开辟rwe
,只要给本类添加方法,协议,属性,都会涉及rwe
的开辟,也就是dirty memory
0x04 - 分类的本质
通过clang -rewrite-objc main.m
命令转成c++
文件查看
通过转换的c++
文件查看分类
是一个category_t
的结构,有名字
,cls
, 实例方法列表, 类方法列表, 协议列表, 属性列表,
通过初始化看到,在编译时,分类的方法名字还是本类的名字,在运行时会替换成分类的名字。
分类中的属性并不会生成方法在instance_methods
和class_meethods
,也没有生成下划线的成员变量,通过runtime
的关联对象
技术 来实现