realizeClassWithoutSwift实现类
在上一篇 类的加载原理(上) 分析 read_images流程中,发现会对类进行一些修复工作,比如修复编译阶段selector的混乱问题、混乱类的处理等;同时会将类的名称与类进行关联,插入对照表中,并更新内存中的类表。
文章的最后找到了 非懒加载类的 加载相关代码:
1. 源码分析
接着上一篇,分析 realizeClassWithoutSwift,上源码:
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
// cls 已经实现,直接 return cls;
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// 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) { ... }
// 递归,加载父类、元类的实现
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
// 设置 父类、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);
}
// 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.2 rw 初始化
rw 初始化。这里涉及到干净内存clean memory和脏内存dirty memory的概念,在 类的底层探究(中)中有说明。
clean memory:指加载后不会发生更改的内存。dirty memory:指在进程运行时会发生更改的内存。
ro(readonly) 属于 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中。
1.3. 递归处理,父类、元类的实现
// 递归,加载父类、元类的实现
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
1.4 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.
// 元类不需要非指针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;
}
// OS_object 类
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
1.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
1.6 methodizeClass
方法化当前的类,向类中添加方法,协议、属性,同时对方法列表进行排序等操作。
// Attach categories 附加类别 - 方法化当前的类,对类进行扩展
// Attach categories
methodizeClass(cls, previously);
2. cls 数据变化
同样的方法,我们在流程中添加一份关键代码,过滤出我们想要研究的类YJPerson。
const char * className = "YJPerson";
if (strcmp(class_getName(cls), className) == 0)
{
printf("hello YJPerson...");
}
设置断点,分别判断在rw初始化前/后 lldb打印cls的数据结构的变化。
2.1 rw 初始化前
运行程序,打印cls的内容:
rw 在初始化之前,类只是有对应的成员结构,内部都还没有赋值,并且 ro无法读取
2.2 rw 初始化后
运行程序,打印cls的内容:
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。
3. methodizeClass分析
realizeClassWithoutSwift完成类的初始化后,rw也已经创建,并能够从cls中获取相关的属性、方法等。那么realizeClassWithoutSwift中最后一行代码methodizeClass方法做了什么呢 ?
3.1 源码分析
首先解读一下源码:
/***********************************************************************
* methodizeClass
* 修复了cls的方法列表,协议列表和属性列表。
* 附加未完成的类别。
* 锁定:runtimeLock必须被调用者持有
**********************************************************************/
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);
...
}
对方法、属性、协议进行处理。见下图:
rwe 为空,很显然,此时还没有对类进行相关的扩展操作,所以 rwe 还没有被创建初始化。此时针对方法、属性、协议的添加操作时无效的
继续向下看,找到了类初始化过程中非常关键的步骤,向类中添加分类方法、协议等
3.2 prepareMethodLists
前面在对 rwe 方法进行处理之前,调用了 prepareMethodLists:
那么 prepareMethodLists方法做了哪些操作呢?上源码:
核心流程,fixupMethodList,根据注释:根据需要对selector进行修复。进入fixupMethodList方法,查看实现流程。见下图:
4. attachCategories 探索
在methodizeClass流程中,还有一行关键代码没有解析!将分类相关内容添加到类中。见下面代码:
objc::unattachedCategories.attachToClass(cls, cls, isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
继续运行代码,进入attachToClass方法中,见下图:
哎呀我去,跟丢了。。。
找到 attachCategories 方法:
// 将方法列表、属性和协议从类别附加到一个类。
// 假设猫的类别都是加载的,并按加载顺序排序,
// 最古老的类别先。
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags)
{
...
}
注释说这个方法就是将类别中的方法、属性和协议添加到类中,那么,这个方法肯定调用了,只是没在按上面调试流程调用,全局搜索 attachCategories:
搜索发现 attachCategories 在 load_categories_nolock 中调用了,继续搜索 load_categories_nolock:
搜索发现 load_categories_nolock 在 loadAllCategories 中调用了,继续搜索 loadAllCategories:
- 梳理一下流程:
- load_images
- loadAllCategories
- load_categories_nolock
- attachCategories
4.1 分类处理初探
创建一个YJPerson的分类,并在分类中添加一个方法sayCate。见下面代码:
@implementation YJPerson (YJ)
- (void)sayCate {
NSLog(@"调用了 : %s", __func__ );
}
@end
attachCategories是向类中添加分类的内容,在load_categories_nolock方法中添加过滤条件,过滤出我们所关心的YJPerson类的分类处理流程!见下图:
运行项目发现没有断住,为什么呢?该方法是由load_images调用的,那么与load方法 是否有关系呢?
修改分类LGPerson(YJ),在其中添加load方法,如下:
@implementation YJPerson (YJ)
+ (void)load {}
- (void)sayCate {
NSLog(@"调用了 : %s", __func__ );
}
@end
重新运行,nice 断住了
lldb 调试,输出 cat 信息,正是 我们自定义的 YJPerson(YJ),且有 sayCate 实例方法
到这里貌似一切谜底都解开了,但是别忘了类和分类中都实现了load方法,也就是 非懒加载的类和分类 处理流程谜底算是解开了。
5. 懒加载类的初始化流程分析
- 非懒加载类的加载流程:
- map_images
- map_images_nolock
- _read_images
- realizeClassWithoutSwift
- methodizeClass
首先把 YJPerson 类和类别 中的 load 方法都注释掉,这时候直接运行项目发现没有走 methodizeClass 方法。啥也没做就跑到 main 里面了
把初始化 YJPerson 的代码松开:
哎呦进来了,由此发现在懒加载的类在进行第一次消息发送时,会对类进行加载。
- 懒加载类的加载流程为:
- lookUpImpOrForward
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
- methodizeClass