上篇文章我们探索了类的本质,发现类的本质是 objc_class,类对象里存储了成员变量、实例方法、属性、协议等,并存在 class_rw_t 和 class_ro_t中,那么本篇来探索一下 rw 和 ro,最后用 runtime 提供的 API 获取一下成员变量、方法等。
1. rw 和 ro 的本质
class_ro_t
class_ro_t存储了当前类在编译期就已确定的 成员变量、实例方法、属性和协议。
这是一块纯净的内存空间,不允许被修改
class_ro_t 可以进行移除(内存不够的时候),再需要的时候可以再从磁盘中加载
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
......
};
根据上篇文章可知,在 class_ro_t 中,方法列表为 baseMethods、协议列表 baseProtocols、属性列表为 baseProperties,成员变量为ivars
class_rw_t
class_rw_t 是运行时生成的,类一经使用就会变成 class_rw_t,它会将class_ro_t里面的东西“拿”过来,再把需要动态修改的分类、属性和方法拷贝进来(成为rwe)。它是可读可写的
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
...
};
class_rw_t 存放于 objc_class 中的 bits (class_data_bits_t) 中,在运行 runtime 的 realizeClass 方法时,会生成 class_rw_t 结构体,该结构体包含类 class_ro_t,并且更新了 data 部分,换成了 class_rw_t 结构体的地址。类的 realizeClass 运行之前:
类的
realizeClass 运行之后:
可以说
class_rw_t 是 class_ro_t 的超集,实际访问的方法、属性也都是访问 class_rw_t 里的内容
class_rw_ext_t
class_rw_ext_t 可以减少内存消耗,苹果在 WWDC2020 里面说过,只有大约 10% 左右的类需要动态修改。所以只有 10% 左右的类里面需要生成 class_rw_ext_t 这个结构体,可以节约很大一部分内存。需要生成 class_rw_ext_t 时,将 ro 里面的东西内容需要修改的内容拷贝出来。
class_rw_ext_t 生成的条件:
- 用过
runtime的API进行动态修改的时候 - 有分类的时候,且分类和本类都是非懒加载类的时候。实现了
+load方法即为非懒加载类
class_ro_t, class_rw_t, class_rw_ext_t 之间的关系:
2.通过 runtime 的 API 探索类的数据结构
获取类的成员变量
// 获取类的成员变量
- (void)getIvarList:(Class)class {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(class, &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
const char *cName = ivar_getName(ivar);
const char *cType = ivar_getTypeEncoding(ivar);
NSLog(@"name = %s, type = %s", cName, cType);
}
// arc 只管理 OC 对象,ivars 是 malloc 出来的
free(ivars);
}
获取类的属性
// 获取类的属性
- (void)getPropertyList:(Class)class {
unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList(class, &outCount);
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
const char *cName = property_getName(property);
const char *cType = property_getAttributes(property);
NSLog(@"name = %s, type = %s", cName, cType);
}
free(properties);
}
获取方法列表
// 获取方法列表
- (void)getMethodList:(Class)class {
unsigned int outCount = 0;
Method *methods = class_copyMethodList(class, &outCount);
for (int i = 0; i < outCount; i ++) {
Method method = methods[i];
NSString *name = NSStringFromSelector(method_getName(method));
const char *cType = method_getTypeEncoding(method);
NSLog(@"name = %@, type = %s", name, cType);
}
free(methods);
}
打印结果如下:
以
v24@0:8@16 为例:
- v: 返回值为 void
- 24 参数总共 24 个字节
- @类型从 0 开始 (消息的接收者)
- : 类型从 8 开始(消息的方法名)
- @类型从 16 开始(方法的参数)
拓展
打印方法的地址:
// 方法的地址
- (void)methodTest:(Class)class {
const char *className = class_getName(class);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(class, @selector(instanceMethod));
Method method2 = class_getInstanceMethod(metaClass, @selector(instanceMethod));
Method method3 = class_getInstanceMethod(class, @selector(classMethod));
Method method4 = class_getInstanceMethod(metaClass, @selector(classMethod));
NSLog(@"%p--%p--%p--%p", method1, method2, method3, method4);
}
打印结果如下:
说明实例方法存在类里,类方法存在元类里
注意:
为什么不用class_getClassMethod获取类方法?源码发现 class_getClassMethod 走的也是 class_getInstanceMethod,说明元类不是为了存储类方法,因为底层没有类方法和实例方法的概念
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
打印方法的实现
// 方法的实现
- (void)methodImpTest:(Class)class {
const char *className = class_getName(class);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(class, @selector(instanceMethod));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(instanceMethod));
IMP imp3 = class_getMethodImplementation(class, @selector(classMethod));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(classMethod));
NSLog(@"%p--%p--%p--%p", imp1, imp2, imp3, imp4);
}
注意
为什么元类可以打印出实例方法的实现,类可以打印出类方法的实现?
源码发现 imp 不存在会走 _objc_msgForward 消息转发
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
lockdebug_assert_no_locks_locked_except({ &loadMethodLock });
imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
结论
-
类的
class_ro_t中存储了成员变量,类对象的class_rw_t中存储了实例方法,属性,协议 -
类方法存储在元类里