探索底层原理,积累从点滴做起。大家好,我是Mars。上文iOS底层原理探索—OC对象的本质中总结了OC对象的底层原理,在结尾补充中简单展示了class底层结构体的内容,今天让我们继续探索class的本质。
在OC中,类对象(class对象)和元类对象(meta-class对象)的本质结构都是struct objc_class
指针,即在内存中就是结构体
Class clas = [NSObject class];
来到class底层源码,我们可以看到:
typedef struct objc_class *Class;
class对象其实是一个objc_class
结构体的指针。因此我们可以说类对象或元类对象在内存中其实就是objc_class
结构体。
相信很多人在查看源码或者看一些底层博客的时候,经常会看到下面一段代码,来讲述class的内部结构:
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
这段源码其实讲述的也是class内部结构,包含成员变量列表
、方法列表
、方法缓存
以及协议列表
。细心的人可能会发现,这段代码里面是有if
判断条件的:
#if !__OBJC2__
在结尾处也有:
OBJC2_UNAVAILABLE
判断条件是非OC2.0版本,也就是说在OC2.0之前的版本中,class底层的结构体中包含上面代码所讲述的,但我们现在所用的最新版肯定是OC2.0版本了,所以这段代码就不再使用了。
那么新的class底层源码是什么样子呢?我们截取objc源码中objc_class
结构体部分内容:
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() {
return bits.data();
}
我们发现objc_class
结构体继承 objc_object
并且结构体内有一些函数,因为这是c++结构体,在c上做了扩展,因此结构体中可以包含函数。注意观察注释掉的Class ISA
这一行代码:
// Class ISA;
我们来到objc_object
内,继续截取部分代码:
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
我们发现objc_object
中有一个isa
指针,那么objc_class
继承objc_object
,也就同样拥有一个isa
指针。继承来了isa
指针,所以上文我们提到了Class ISA
也就被注释掉了。
看完源码,那么我们来分析一下class底层的objc_class
结构体:
可以看到结构体中只包含isa
、superclass
、cache
和bits
。而在上文的源码中有这样一个方法:
class_rw_t *data() {
return bits.data();
也就是说class_rw_t
是通过bits
调用data
方法得来的,我们来到data
方法内部实现:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
我们可以看到,data
函数内部仅仅对bits
进行&FAST_DATA_MASK
操作,会得到class_rw_t
这样一个结构体:
rw代表readwrite,可读可写
t代表table,列表
struct class_rw_t
结构体中就包含了方法列表、属性列表以及协议列表,这些都是可读可写的。其中还包含一个struct class_ro_t
的结构体:
ro代表readonly,只读
struct class_ro_t
的结构体中包含了instance对象占用的内存空间、类名以及成员变量列表,当然这些都是只读的。
最后我们用一张图来总结一下struct objc_class
的结构:
下一站底层探索走进KVO的本质,预留问题:
1、KVO的本质是什么?
2、如何手动触发KVO?
更多技术知识请关注公众号
iOS进阶