iOS底层探索 -- 类的结构分析
2020-09-14 本文已影响0人
iOS小木偶

之前研究了isa的本质,在研究过程中经常会看到这张isa指针流程和类继承的关系图 今天我们就顺着这张图开始研究类
关于类 元类 根源类
举例说明:FQPerson继承于NSObject ,FQTeacher继承于FQPerson
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc] init];
FQPerson * person = [FQPerson alloc];
FQTeacher * teacher = [FQTeacher alloc];
NSLog(@"person:%@ teacher:%@",person,teacher);
}
return 0;
}
由lldb断点调试

在我们尝试对NSObject的对象obj的isa追踪时,我们发现一个有趣现象,我们尝试打印了几个不同的地址,但是打印出来的结果均为
NSObject
。这是否代表一个类有多个类地址?
对此,我们做另一个尝试

我们通过不同的方式获取了FQPerson的类地址,但是结果是相同的,所以证明一个类只有一个类地址。
那么为什么上面的NSObject为何有多个地址呢?
其实就是最初继承关系图中的元类、根元类。
同时打印person信息验证

objc_class 和 objc_object
我们知道类的底层是objc_class 而对象是以objc_object为模板
通过源码分析
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();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
objc_class是一个继承于objc_object
而objc_object中含有一个isa
这就是为什么所有对象和类都有isa属性
同时也可以说万物皆对象
内存偏移
在我们对类对象分析之前,我们开始了解一个概念 - 内存偏移
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p -- %p -- %p", &c, &c[0], &c[1]);
NSLog(@"%p -- %p -- %p", d, d+1, d+2);
打印结果
0x7ffeefbff530 -- 0x7ffeefbff530 -- 0x7ffeefbff534
0x7ffeefbff530 -- 0x7ffeefbff534 -- 0x7ffeefbff538
由于数据在内存中的存储是连续的,所以我们在知道偏移量的情况下,我们可以直接通过内存地址获取数据。
所以如果我们想分析class_data_bits_t
的内部数据,我们首先需要知道cache_t
的大小
通过对cache_t
的源码分析,我们可以计算出大小为8+4+2+2 = 16字节,
同时加上 isa 和 superclass 的各8字节,class_data_bits_t
应当从首地址的后32字节开始
类的结构分析
通过上面的内存偏移的方法,我们可以尝试获取class_data_bits_t
中的数据

可以发现,我们已经成功获取到了
class_data_bits_t
的信息
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 *extAlloc(const class_ro_t *ro, bool deep = false);
public:
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 *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->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 *>()->properties;
} else {
return property_array_t{v.get<const class_ro_t *>()->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 *>()->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
}
}
};
对类来说,最重要的是属性和方法,我们尝试下继续获取属性和方法


总结
类的本质是一个结构体对象,而结构体对象可以通过属性地址来分析。