iOS class_rw_t 和 class_ro_t 及cla
先来看看 class_rw_t
和class_ro_t
的结构
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t version;
uint16_t witness;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
可以了解到class_rw_t
和class_ro_t
中都有方法,属性,协议
这些东西,而且class_rw_t
中还有一个只读的class_ro_t
,class_ro_t
中多了ivars
成员变量等
class_rw_t
我们在类结构中见过,通过bits.data()
可以得到
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
}
在前面一篇文章iOS 捋一捋Category加载流程及+load中,我们知道运行时会对类,分类等做一些处理。在runtime
的入口函数_objc_init
中,往后走会调用realizeClassWithoutSwift
这个方法。
掏出一部分源码过来分析,我们在这里打一个Person
类的断点
ro = (const class_ro_t *)cls->data();
if (strcmp(tempChar, "Person")==0) {
}
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
这里你会发现cls->data()
是用ro
接收的,按我们看的源码结构不是应该是rw
吗。
我们打印看看ro
有些什么
(lldb) p *ro
(const class_ro_t) $0 = {
flags = 128
instanceStart = 8
instanceSize = 9
reserved = 0
ivarLayout = 0x0000000000000000
name = 0x0000000100003eeb "Person"
baseMethodList = 0x0000000100008350
baseProtocols = 0x0000000000000000
ivars = 0x00000001000083a0
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000083c8
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $0.ivars
(const ivar_list_t *const) $1 = 0x00000001000083a0
(lldb) p *$1
(const ivar_list_t) $2 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 1
first = {
offset = 0x0000000100008438
name = 0x0000000100003f60 "_sex"
type = 0x0000000100003faa "c"
alignment_raw = 0
size = 1
}
}
}
(lldb) p $0.baseMethodList
(method_list_t *const) $3 = 0x0000000100008350
(lldb) p *$3
(method_list_t) $4 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 24
count = 3
first = {
name = "sayYes"
types = 0x0000000100003f65 "v16@0:8"
imp = 0x0000000100003c80 (KCObjcTest`-[Person sayYes] at Person.m:14)
}
}
}
(lldb) p $4.get(1)
(method_t) $5 = {
name = "sex"
types = 0x0000000100003f97 "c16@0:8"
imp = 0x0000000100003cc0 (KCObjcTest`-[Person sex] at Person.h:14)
}
(lldb) p $4.get(2)
(method_t) $6 = {
name = "setSex:"
types = 0x0000000100003f9f "v20@0:8c16"
imp = 0x0000000100003ce0 (KCObjcTest`-[Person setSex:] at Person.h:14)
}
(lldb) p $0.baseProperties
(property_list_t *const) $7 = 0x00000001000083c8
(lldb) p *$7
(property_list_t) $8 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
first = (name = "sex", attributes = "Tc,N,V_sex")
}
}
发现成员变量,属性,方法都已经在里面了。
那我们可以知道class_ro_t
存储了当前类在编译期就已经确定的属性、方法以及遵循的协议等, 而到了运行时这里做了class_rw_t
的处理,这里会生成class_rw_t
结构体,将class_rw_t
的ro
赋予class_ro_t
,并且更新data部分,换成class_rw_t
。
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
而后会有分类的处理,分类的信息处理则都是放在rw
中的。
总结一下class_rw_t 和 class_ro_t
class_ro_t
存储了类在编译期已经确定的属性、方法以及遵循的协议,类结构中bits
一开始存储的是class_ro_t
,运行时会创建class_rw_t
,并且把class_ro_t
赋值给class_rw_t
的ro
,然后刷新bits
, class_ro_t
是只读的,运行时我们给class
添加方法等操作,都是通过class_rw_t
来实现的。
接着我们看看class_copyIvarList & class_copyPropertyList
当我们@property(nonatomic,copy)NSString *name;
声明一个属性的时候,我们都知道编译器会帮我们创建实例变量_name
,并且声明且实现name
属性的setter、getter
方法。
即@property = ivar + getter + setter;
再看一下这俩方法的源码
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
const ivar_list_t *ivars;
Ivar *result = nil;
unsigned int count = 0;
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
ASSERT(cls->isRealized());
if ((ivars = cls->data()->ro->ivars) && ivars->count) {
result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
for (auto& ivar : *ivars) {
if (!ivar.offset) continue; // anonymous bitfield
result[count++] = &ivar;
}
result[count] = nil;
}
if (outCount) *outCount = count;
return result;
}
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
checkIsKnownClass(cls);
ASSERT(cls->isRealized());
auto rw = cls->data();
property_t **result = nil;
unsigned int count = rw->properties.count();
if (count > 0) {
result = (property_t **)malloc((count + 1) * sizeof(property_t *));
count = 0;
for (auto& prop : rw->properties) {
result[count++] = ∝
}
result[count] = nil;
}
if (outCount) *outCount = count;
return (objc_property_t *)result;
}
发现一个是从ro
的ivars
里取, 一个是从rw
的properties
里取。
我们正常使用property
也会生成对应的实例变量,所以 ro
的ivars
也可以查到,
so
-
class_copyIvarList
获取类中的所有实例变量信息,从class_ro_t
的ivars中获取 -
class_copyPropertyList
获取类中的属性信息,从class_rw_t
的properties
中获取
验证一下
/// person里增加
{
NSString *_nickName;
}
@property(nonatomic,assign)BOOL sex;
/// 打印一下
unsigned int ivarsCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &ivarsCount);
for (int i = 0; i<(int)ivarsCount; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *s = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSLog(@"class_copyIvarList -- %@",s);
}
NSLog(@"----------------------------------------");
unsigned int propertiesCount = 0;
objc_property_t *properties = class_copyPropertyList([Person class], &propertiesCount);
for (int i = 0; i<(int)propertiesCount; i++) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
NSString *s = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSLog(@"class_copyPropertyList -- %@",s);
}
结果如下
[20877:1150546] class_copyIvarList -- _nickName
[20877:1150546] class_copyIvarList -- _sex
[20877:1150546] ----------------------------------------
[20877:1150546] class_copyPropertyList -- sex
打印结果验证了理论。