所用版本:
- 处理器: Intel Core i9
- MacOS 12.3.1
- Xcode 13.3.1
- objc4-838
建议先看下
OC底层探索(十三): 类的加载(一)
OC底层探索(十四): 类的加载(二)
接上次继续看realizeClassWithoutSwift
实现类方法
realizeClassWithoutSwift
remapClass自定义类跟断点走, 可发现先走remapClass
对类指针做修正
[ 第一步:生成数据 ]
生成ro数据-
auto ro = (const class_ro_t *)cls->data();
: 取转换cls
中的data
数据,再强制转为class_ro_t *
, 赋值给ro
data()方法
看下内部可看出, data() 内部: bits & FAST_DATA_MASK
与操作拿到data()
, 而这里的data()
是个地址指针 class_rw_t *
类型, 获取之后外层再强制转成class_ro_t *
指针。
留意下, 几乎所有引用到class_ro_t
的地方都会使用了const
关键字做修饰, 苹果希望ro像是一个静态, 不愿意被外界随意修改的属性, 而rw
无.
关于那个强转稍微说下, class_rw_t
与class_ro_t
2个结构体虽然有点差距, 但是ro
需要的部分rw
都可以找到, 可以说是一一给赋值关系。
举个例子: 设置一个Method
方法
其中class_getInstanceMethod
返回是一个Method *
类型
Method class_getInstanceMethod(Class cls, SEL sel)
OK没问题, 那么我们只用 Method
中 method_name
, method_imp
写成一个新的结构体
发现也是正常运行, 通过打印信息可发现, 只有2个成员。这个是就跟class_rw_t *
强转class_ro_t *
一样, 只取对应信息强转调用。但是也要留意两者是格式对应或包含关系, 如果格式地址无法解析或者不匹配到位, 会发生解析不出来情况。
继续往下走, 可发现
-
rw
初始化 - 将
ro
数据存入rw
中 - 设置 flags标识符uint32_t, 1<<31 | 1<<19 | 1<<0, 31位表示
rw
是否已经初始化完毕RW_REALIZED
,19位表示rw是否初始化中RW_REALIZING
,元类为1,非元类为0。 - 最后将
rw
存入cls
的data
中( 话说苹果设计这么多叫data的, 不嫌绕么:) )
[ 第二步:完善父类链元类链 ]
完善父类链元类链- 递归方法
realizeClassWithoutSwift
完善父类链, 元类链。
- 如果是元类, 更改isa类型为raw isa( 纯isa), 回顾下
isa联合体位域
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
其中 nonpointer
:表示是否对isa指针开启优化。0:纯isa指针,1:不只是类对象地址,isa中包含了类信息、对象的引用计数等。其中大部分的类都是nonpointer类
,元类不是nonpointer
。
接下来是对几种特殊情况的纯指针处理
特殊情况的纯指针isa关系
- 环境变量为DisableNonpointerIsa, 禁止NonpointerIsa
- 非hackedDispatch, 且当前为OS_object类,
- 继承链相关需要为纯指针等
也都要, 要将isa改变成raw isa
关联父类, 元类- 父类链, 元类链处理完之后进行关联
如果是父类需要更多的内存空间, 会做个ro做调整
reconcileInstanceVariables reconcileInstanceVariables gdb_objc_class_changed找最大的实例变量做个重新对齐, 并与父类ro
作比较, 如果没超出范围直接返回, 超出就重新设置ro
。
举个例子: 父类SRTest
, 子类SRTestSub
, 父类里面2个NSString *
, 子类什么也没有
运行一下
例子
- 父类 isa指针 + 2 * 指针 = 8 + 2 * 8 = 24, 系统16字节对齐结果 =
32字节
- 子类isa = 8字节, 但是实际依然要开辟 =
32字节
有点父欠子还那味儿了, 回到realizeClassWithoutSwift
继续看
ro计算内存大小存到缓存中, 下次就从缓存中取
处理标识处理一些Flag标志位
- 拷贝
ro
的一些flags
到rw
-
RO_HAS_CXX_STRUCTORS
标记c++构造方法
。 -
RO_HAS_CXX_DTOR_ONLY
标记c++析构方法
。 - 处理一些禁止标志位: 自己禁止关联对象或者父类禁止关联对象 (父类如果禁止,子类同步禁止), 则同步
RW_FORBIDS_ASSOCIATED_OBJECTS
标记。
父类子类进行关联
addSubclass-
改变父子类data()中对应类字段, 进行关联
- 子类:
nextSiblingClass
- 父类:
firstSubclass
- 子类:
-
父类如果有CXX相关, 子类同步
-
父类如果设置不允许预缓存, 子类也不允许
-
父类如果设置不允许内联sel, , 子类也不允许
-
父类如果的isa纯指针, 子类同步。
...不得不说, 父亲管的是真严格啊 :), 根类是单独设置的, 也稍微看一下
addRootClass/***********************************************************************
* _firstRealizedClass
* The root of all realized classes
**********************************************************************/
static Class _firstRealizedClass = nil;
相邻为设置为nil, 自己设置为NSObject
[ 第三步:methodizeClass ]
继续往下走可看到进行走方法methodizeClass
methodizeClass
主要从ro
中读取方法列表(包括分类中的方法)
, 属性列表
, 协议列表
等 赋值给rw
,并返回cls
。 看下内部
- 先获取
ro
,rw
,rwe
- rwe 存在情况下
- rwe 添加方法列表
- rwe 添加属性列表
- rwe 添加协议列表
- 根元类需要添加
initialize
方法。
再往下看都是分类加载内容, 放在下一篇: OC底层探索(十六): 类的加载(四)
网友评论