在学习Class结构之前,我们先了解苹果在iOS14(iOS14, MacOS Big Sur, iPadOS14)之后优化了类在内存的结构,优化详情请看WWDC20,如果喜欢文字版本也可以看看小专栏的**WWDC20 10163 - Advancements in the Objective-C Runtime。**
iOS14之前Class的结构
objc_class中的bits & FAST_DATA_MASK找到class_rw_t,class_rw_t中的ro指向了class_ro_t。
iOS14之后Class的结构
苹果为什么要做优化呢?在苹果的调查中只有10%的类实际会存在动态的更改行为,如动态添加方法、 Category 方法等,剩下的90%是不需要这些功能的,在老的class_rw_t存储了所有的方法、属性、协议等,这样造成了内存浪费。所以苹果在iOS14对类结构进行了优化。
分析Class的结构需要考虑是否有运行时操作如分类和动态添加方法等,另一个就是没有运行时操作的。
进行了运行时操作
class_rw_t中的ro_or_rw_ext指向class_rw_ext_t,class_rw_ext_t中ro指向了class_ro_t;
没有运行时操作
class_rw_t中的ro_or_rw_ext指向class_rw_ext_t,class_rw_ext_t中ro指向了class_ro_t;
iOS14苹果给class_rw_t增加了一个扩展class_rw_ext_t,这样做法是在运行时有些方法存在分类、动态添加方法,那么苹果就把这些数据存储在class_rw_ext_t结构体中,并把class_rw_ext_t中的ro变量指向class_ro_t结构体,然后将class_rw_t中的ro_or_rw_ext指向扩展结构体class_rw_ext_t;
**class_ro_t:**是只读的,存放的是编译时就决定的字段信息。
class_rw_t:是在 runtime 时才创建的,它会先将class_ro_t
的内容拷贝一份,再将类的分类的属性、方法、协议等信息添加进去,之所以要这么设计是因为 Objective-C 是动态语言,你可以在运行时更改它们方法,属性等,并且分类可以在不改变类设计的前提下,将新方法添加到类中。
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容,这里只列出了方法的
从上一篇文章我知道了,Objective-C中对象在源码中是objc_class,下面是我整理的源码,省略了一些其他的代码,保留了主要代码。
objc_class
struct objc_object {
private:
isa_t isa;
}
struct objc_class : objc_object {
isa_t isa; //因为objc_class 继承自objc_object,所以我把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);
}
};
class_data_bits_t
struct class_data_bits_t {
public:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
};
class_rw_t
从源码中可以知道iOS14以后ro、methods、prperties和protocols都变成了成员方法,需要通过方法操作才能拿到想要的数据,这里以methods方法为例:
v.is<class_rw_ext_t *>()为真从class_rw_ext_t中去除方法列表,如果为否从class_ro_t中拿出方法并把一位数组组装成二维数组protocol_array_t;
struct class_rw_t {
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
public:
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
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 *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
}
class_rw_ext_t
iOS14以后增加结构体,用于存储动态添加方法、分类方法。
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
因为class和meta-class都是objc_class结构,所以这里只做了class 方法打印了
DEMO地址,这里是可以被编译的objc4-818.2源码。