在objc2之后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
}
这里的bits是个共用体union
bits & mask之后获得一个结构体 class_rw_t:
struct class_rw_t {
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;
}
const class_ro_t *ro
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_rw_t顾名思义,拥有读写权限,ro就是只读的意思,ivars放在class_ro_t里面,所以ivar在编译时期已经是固定的了,之后不能动态的添加更改,但是property,和methods在rw_t里面,所以可以允许动态添加。
还要就是instanceSize也是放在class_ro_t里的,所以类的大小是在编译时期已经决定的了。可以这么理解,ivars放在class_rw_t,那有可能以后获得size就错误。
二:那为啥会是这样的设计呢?
其实也不难。成员变量,属性其实都是属于它的实例的,不同的实例,他们的成员变量都是不同的,所以他们都存储在所在实例的堆空间中,互不干扰,而且加入程序运行后再动态添加属性,那该类原来的子类的空间布局是不是也要相应的做出改变呢?。但是方法确实公用的,不同的实例所调用的方法都是一样的,而且我们也知道,方法其实是存在于代码区的,所以即使动态的添加方法,也不影响原来架构和功能。