直接开搞,打开objc
源码, 搜索Class
, 发现Class
实质上是结构体objc_class
的重定义, 换句话说类的本质就是结构体。继续深入,找到object_class
结构体的定义部分:(objc_object
是id
类型的, 所以这里就找到底了)
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();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
...
}
这里我们可以看出类的结构中主要包含isa
、superclass
、cache
、bits
以及一个bits.data
, 可以看出类的数据都存在bits.data
属性中,继续研究这个属性,发现这个属性是class_rw_t
类型的,我们继续查看class_rw_t
的结构,
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro; //只读文件
method_array_t methods; //方法
property_array_t properties;//属性
protocol_array_t protocols;//协议
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
...
}
发现这里面存的有方法methods
,属性properties
,协议protocols
,还有一个类型为class_ro_t
的文件,注意这个文件是用const
进行修饰的,也就是说这个文件是不可以进行更改,是一个只读的文件!我们继续查看这个类型class_ro_t的结构,发现了这个文件里面主要包含下面这些成员变量
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;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
到这里,按照源码一层层深入,类的结构已经大致查看完了,思维导图总结如下:

看完类的结构,我们需要注意一个细节:
class_ro_t
类里面的属性成员变量ivars
是用const
修饰的!也就是说,在类的结构中,ivars
这个属性是不可更改的,如果使用Runtime的API注册和实现一个类,必须注意添加成员变量的顺序
//1、自定义一个类
Class BMClass = objc_allocateClassPair([NSObject class], @"BMClass", 0);
//2、给类添加一个成员变量ivar
NSString * name = @"name";
class_addIvar(BMClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
//3、注册这个类
objc_registerClassPair(BMClass);
添加成员变量class_addIvar
操作必须要在类的注册objc_registerClassPair
之前,如果类已经注册完毕之后,ivar就添加不进去了!会导致你获取这个成员变量获取不到!这也是一个常见的知识坑点
网友评论