realizeClassWithoutSwift引入
如何找到执行class的rw、ro、rwe操作入口
- 通过最直接的方式:
设置断点+写入打印代码
,动态调试 - 只关心自定义的class(LGPerson操作),查看测试代码执行情况
- 对
_read_images
内所有关于class操作的地方进行监测,Realize non-lazy classes、realize future classes
<!-- 实现非懒加载的类 -->
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
<!-- 插入的测试代码 -->
// KC 专用
const char *mangledName = cls->nonlazyMangledName();
const char *LGPersonName = "LGPerson";
// 节约内存 速度
if (strcmp(mangledName, LGPersonName) == 0) {
// 普通写得类 他是如何
printf("%s Realize non-lazy classes -KC: 要研究的: - %s\n",__func__,mangledName);
}
<!-- 插入的测试代码 -->
// KC 专用
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
<!-- 实现新解析的未来类 -->
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
<!-- 插入的测试代码 -->
// KC 专用
const char *mangledName = cls->nonlazyMangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
// 普通写得类 他是如何
printf("%s -resolvedFutureClasses-KC: 要研究的: - %s\n",__func__,mangledName);
}
<!-- 插入的测试代码 -->
// KC 专用
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
LGPerson *p = [LGPerson alloc];
}
}
// 运行工程打印如下,并没有执行到插入的测试代码中
2021-08-14 10:46:59.302509+0800 KCObjcBuild[32336:3938795] Hello, World!
Program ended with exit code: 0
<-- 调整案例代码 -->
@implementation LGPerson
+ (void)load{
NSLog(@"load");
}
@end
// 运行工程打印如下,执行到插入的测试代码中
_read_images Realize non-lazy classes -KC: 要研究的: - LGPerson
2021-08-14 11:00:29.035752+0800 KCObjcBuild[32420:3947246] load
2021-08-14 11:00:29.036473+0800 KCObjcBuild[32420:3947246] Hello, World!
Program ended with exit code: 0
找到的入口在Realize non-lazy classes
,然后向下执行的是realizeClassWithoutSwift
函数
realizeClassWithoutSwift分析
realizeClassWithoutSwift
方法中有ro、rw
的相关操作。这个方法在消息慢速查找流程
中有所提及。其主要作用是实现类
,将类的data数据加载到内存
中。主要步骤如下
- 类的初始化
- 读取
data数据
,并设置ro、rw
- 递归调用
realizeClassWithoutSwift
完善继承链
- 通过
methodizeClass
方法化类
类的初始化
if (!cls) return nil;
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
首先判断类是否存在,如果不存在直接返回nil,然后再判断这个类是否已经验证实现,如果实现则返回当前类,主要是因为这个地方也存在递归实现元类与父类,根类的父类为nil、元类的isa指向自己
,所以这样可以保证类只会被初始化一次
读取data数据,并设置ro、rw
// fixme verify class is not in an un-dlopened part of the shared cache?
// 获取类中的RO数据
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// 元类:rw数据已经写入完成
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
//类:1、开辟rw空间
rw = objc::zalloc<class_rw_t>();
//2、将ro写入rw
rw->set_ro(ro);
//3、flags标志位设置,元类为1,非元类为0
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
//4、设置rw数据
cls->setData(rw);
}
读取class的data数据,并将其强转为ro
。rw的初始化
,ro拷贝一份到rw中的ro
- ro:
clean memory
,在编译期间确定的内存空间,只读不可改变,其存储着类名称、属性、协议、方法、实例变量等 - rw:
dirty memory
,在 运行时 生成,可读可写,由于其动态性,可以往类中添加属性、方法、协议 - rwe :
类的额外信息
,只有不到10%的类真正的更改了他们的方法,并不是每一个类都需要插入数据,进行修改的类很少,避免资源的消耗,所以就有了rwe。rwe中存储的一般是分类的信息、动态添加的方法等
递归调用realizeClassWithoutSwift完善继承链
//递归调用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
- 递归调用
realizeClassWithoutSwift
并设置父类、元类 - 设置父类和元类的isa指向
- 通过
addSubclass
和addRootClass
设置父子的双向链表指向关系,即父类中可以找到子类,子类中可以找到父类
通过methodizeClass方法化类
// Attach categories 附加类别 -- 疑问:ro中也有方法列表 rw中也有方法列表,下面这个方法可以说明
// 将ro数据写入到rw
methodizeClass(cls, previously);
return cls;
通过methodizeClass
方法,从ro中读取方法列表
(包括分类中的方法)、属性列表
、协议列表
赋值给rw并返回cls
methodizeClass分析
methodizeClass
源码实现如下,主要分以下几部分
- 将
属性列表、方法列表、协议列表
等贴到rwe
中 - 附加
分类
中的方法
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();
......
// Install methods and properties that the class implements itself.
//将属性列表、方法列表、协议列表等贴到rwe中
// 将ro中的方法列表加入到rwe中
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
//将属性添加到rwe中
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
//将协议添加到rwe中
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 {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
......
rwe的逻辑
方法列表添加至rwe逻辑如下
- 获取ro的
baseMethods
- 通过
prepareMethodLists
方法排序 - 对rwe进行处理即通过
attachLists
插入
方法排序prepareMethodLists方法
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
runtimeLock.assertLocked();
// 将方法列表添加到数组中。
// 重新分配未固定的方法列表。
// 新方法预先添加到方法列表数组中。
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
的源码实现,内部是通过fixupMethodList
方法排序
fixupMethodList修复方法列表,即重新排序
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
/// 按选择器地址排序。
// 不要尝试对小列表进行排序,因为它们是不可变的。
// 不要尝试对非标准大小的大列表进行排序,如 stable_sort
// 不会正确复制条目。
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
// 将方法列表标记为唯一且已排序。
// 不能标记小列表,因为它们是不可变的。
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
向fixupMethodList
中加入自定义打印,验证方法排序
// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("上面 : %s - %p\n",name,meth.name());
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
// Sort by selector address.
// Don't try to sort small lists, as they're immutable.
// Don't try to sort big lists of nonstandard size, as stable_sort
// won't copy the entries properly.
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
printf("****************");
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("下面 : %s - %p\n",name,meth.name());
}


排序后的地址是由小到大排列
的,验证了fixupMethodList
函数的排序功能。
关于这部分的流程
_read_images->read_class(地址和名称)->relizeClassWithoutSwift(对于ro、rw的操作,对superclass、isa也做了处理)->methodizeClass->prepareMethodLists->fixupMethodList (方法排序)
懒加载类与非懒加载类
开篇我们在寻找class的操作入口
时,给LGPerson
添加了+ (void)load
方法会执行到非懒加载类
源码方法中。所以懒加载类
和非懒加载类
的区别就是是否实现了+load方法
- 非懒加载类即程序启动时就会进行以上
ro、rw、排序
等耗时操作,效率很低。 - 苹果为了提高效率采用了
按需加载
,也就是懒加载类
,等需要时再加载。
为什么实现load
方法就会变成非懒加载类?
原因是load
方法会提前加载(如果类存在,load方法会在load_images中调用)
懒加载类在什么时候加载? 答案:在调用方法
的时候加载
调试验证懒加载类的加载时机
- 注释掉
LGPerson
中的+load
方法,并在main中实例化person
int main(int argc, const char * argv[]) {
@autoreleasepool {
// class_data_bits_t
LGPerson * person = [LGPerson alloc];
}
return 0;
}
- 在
realizeClassWithoutSwift
方法中添加该类的断点

- 打印堆栈信息查看懒加载类调用时机
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001002ff859 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x0000000100008298, previously=0x0000000000000000) at objc-runtime-new.mm:2654:9 [opt]
frame #1: 0x000000010030fe08 libobjc.A.dylib`realizeClassMaybeSwiftMaybeRelock(cls=0x0000000100008298, lock=<unavailable>, leaveLocked=true) at objc-runtime-new.mm:2920:9 [opt]
frame #2: 0x0000000100307103 libobjc.A.dylib`lookUpImpOrForward [inlined] realizeClassMaybeSwiftAndLeaveLocked(cls=0x0000000100008298, lock=<unavailable>) at objc-runtime-new.mm:2943:12 [opt]
frame #3: 0x00000001003070f6 libobjc.A.dylib`lookUpImpOrForward [inlined] realizeAndInitializeIfNeeded_locked(inst=0x00000001000082c0, cls=0x0000000100008298) at objc-runtime-new.mm:6384 [opt]
frame #4: 0x00000001003070f6 libobjc.A.dylib`lookUpImpOrForward(inst=0x00000001000082c0, sel="alloc", cls=0x0000000100008298, behavior=<unavailable>) at objc-runtime-new.mm:6499 [opt]
frame #5: 0x00000001002ededb libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
frame #6: 0x0000000100003bea KCObjcBuild`main(argc=<unavailable>, argv=<unavailable>) at main.m:42:23 [opt]
frame #7: 0x00007fff20369621 libdyld.dylib`start + 1
得出结论
此时bt下查看函数调用栈,发现了lookUpImpOrForward
函数,所以懒加载类的加载是在消息的慢速查找流程中调用
懒加载类
和非懒加载类
的加载时机
如下图

分类的本质探索
下面讨论分类是如何加载到类中
的,以及分类和类搭配使用的情况。在main中定义LGPerson
的分类LG
// 这里给分类添加了协议<NSObject>
@interface LGPerson (LG) <NSObject>
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod2;
+ (void)cate_classMethod3;
@end
@implementation LGPerson (LG)
- (void)cate_instanceMethod1{
NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
NSLog(@"%s",__func__);
}
+ (void)cate_classMethod3{
NSLog(@"%s",__func__);
}
@end
探索分类的本质,有以下三种方式
- 通过
clang -rewrite-objc main.m -o main.cpp
- 通过Xcode文档搜索
Category
- 通过objc源码搜索
category_t
方式一: 使用clang -rewrite-objc main.m -o main.cpp
将main.m文件转成 main.cpp,查看底层编译
struct _category_t {
const char *name; //名字为LG
struct _class_t *cls;//主类名
const struct _method_list_t *instance_methods;//实例方法列表
const struct _method_list_t *class_methods;//类方法列表
const struct _protocol_list_t *protocols;//协议
const struct _prop_list_t *properties;//属性
};
static struct _category_t _OBJC_$_CATEGORY_LGPerson_$_LG __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"LGPerson",
0, // &OBJC_CLASS_$_LGPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LGPerson_$_LG,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LGPerson_$_LG,
// 添加的<NSObject>协议
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_LGPerson_$_LG,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LGPerson_$_LG,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_LGPerson_$_LG,
};
// 添加的协议_OBJC_PROTOCOL_NSObject
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_LGPerson_$_LG __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSObject
};
- 分类被翻译成
c++文件
后,可以看出_category_t
是一个结构体 - 分类没有
元类
的概念,所以instance_methods
存储实例方法,class_methods
存储类方法 - 我们可以找到
实例方法
和类方法
的实现代码,但是分类中定义的属性没有相应的set、get方法(我们可以通过关联对象
来设置,关于如何设置关联对象,我们将在后续的扩展中进行说明),所以分类不会自动生成方法
- 在结构体中我们没有找到
ivar
的列表,所以分类中不能声明成员变量
方式二: 通过Xcode文档搜索 Category

去底层objc源码
中查阅objc_category
如下
struct objc_category {
char * _Nonnull category_name OBJC2_UNAVAILABLE;
char * _Nonnull class_name OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
}
方式三:通过objc源码搜索 category_t
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
分类加载的引入
其中查看methodizeClass
的源码实现,可以发现类的数据和分类的数据
是分开处理的,主要是因为在编译阶段
就已经确定好了方法的归属位置(即实例方法存储在类中,类方法存储在元类中),而分类是后面才加进来的
// 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);
其中分类需要通过attatchToClass
添加到类,然后才能在外界进行使用,在此过程我们了解到分类的加载主要分为三步:
- 分类数据加载时机:根据类和分类
是否实现load方法
来区分不同的时机 -
attachCategories
准备分类数据 -
attachLists
将分类数据添加到主类中
我们在源码中了解到与分类加载相关的线路有两条
attatchToClass -> attachCategories
-
load_categories_nolock -> attachCategories
其源码实现我们下节课再探索...
网友评论