Objective-C类结构分析(class_rw_t)
上一篇分析了cache_t的结构和怎么缓存方法的,这里继续分析objc_class中的class_data_bits_t,搜索可以找到主要结构如下
struct class_data_bits_t {
friend objc_class;
uintptr_t bits;
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
bool isAnySwift() {
return isSwiftStable() || isSwiftLegacy();
}
};
可以看出,结构体里面只有一个bits,然后主要方法就是获取class_rw_t,和判断是不是Swift的类。
看标题就知道,这篇文章的重点就是class_rw_t,源码中搜索找到结构体定义
struct class_rw_t {
uint32_t flags; //标记位
uint16_t witness; //存放cls地址 缓存range的索引
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass; //第一个子类
Class nextSiblingClass; //兄弟类
}
这里主要关注ro_or_rw_ext,使用的时候,会通过联合指针转换成ro_or_rw_ext_t
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
这个里面可能存储着const class_ro_t或者class_rw_ext_t,但只有class_ro_t的时候,就存储class_ro_t,当两者同时存在的时候,就存储class_rw_ext_t。也就是需要的时候才会加载class_rw_ext_t进行分配内存。
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods();
if (list) {
if (deepCopy) list = list->duplicate();
rwe->methods.attachLists(&list, 1);
}
// See comments in objc_duplicateClass
// property lists and protocol lists historically
// have not been deep-copied
//
// This is probably wrong and ought to be fixed some day
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
set_ro_or_rwe(rwe, ro);
return rwe;
}
搜索extAllocIfNeeded,会发现在以下方法中调用
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,int flags)
BOOL class_addProtocol(Class cls, Protocol *protocol_gen)
static bool
_class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attrs, unsigned int count, bool replace)
可以看出,当类的方法、协议、属性,除类本身外有额外添加的时候就会加载class_rw_ext_t分配内存。
class_rw_ext_t 结构如下
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;
};
这里面存储着,*_array_t类型的方法、属性、协议。
char *demangledName;,可以找到如下方法,应该是Swift类OC化的类名。
const char *
protocol_t::demangledName()
{
if (!hasDemangledNameField())
return mangledName;
if (! _demangledName) {
char *de = copySwiftV1DemangledName(mangledName, true/*isProtocol*/);
if (! OSAtomicCompareAndSwapPtrBarrier(nil, (void*)(de ?: mangledName),
(void**)&_demangledName))
{
if (de) free(de);
}
}
return _demangledName;
}
version应该是版本,这里如果是7的话当前类为元类,0的话为普通类
rwe->version = (ro->flags & RO_META) ? 7 : 0;
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;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
#if __has_feature(ptrauth_calls)
method_list_t *ptr = ptrauth_strip((method_list_t *)baseMethodList, ptrauth_key_method_list_pointer);
if (ptr == nullptr)
return nullptr;
// Don't auth if the class_ro and the method list are both in the shared cache.
// This is secure since they'll be read-only, and this allows the shared cache
// to cut down on the number of signed pointers it has.
bool roInSharedCache = objc::inSharedCache((uintptr_t)this);
bool listInSharedCache = objc::inSharedCache((uintptr_t)ptr);
if (roInSharedCache && listInSharedCache)
return ptr;
// Auth all other small lists.
if (ptr->isSmallList())
ptr = ptrauth_auth_data((method_list_t *)baseMethodList,
ptrauth_key_method_list_pointer,
ptrauth_blend_discriminator(&baseMethodList,
methodListPointerDiscriminator));
return ptr;
#else
return (method_list_t *)baseMethodList;
#endif
}
};
这里面主要存储着类的信息,包含大小、类名、成员变量、属性列表、方法列表、协议等信息。还有就是class_ro_t是只读的,存储的信息在编译时期就已经确定了,不能再更改,并且必要的时候可以清除,需要用的时候重磁盘中加载就好了。
class_rw_t 是可读可写的,它信息的存储是在运行的时候,需要用到的时候才会写入,并且一直存在于内存中。因为class_rw_ext_t没有成员变量列表,这就是为什么分类中不能添加成员变量的原因。
继续看下 *_array_t 和 *_list_t,查找代码可以找到如下代码
class *_array_t : public list_array_tt<*_t, *_list_t, *_list_t_authed_ptr>
*_array_t 继承于list_array_tt,里面存放着*_list_t。相当于一个二维数组,结构如下
class list_array_tt {
struct array_t {
uint32_t count;
Ptr<List> lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count*sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
}
*_list_t里面存储着各自的*_t的对象,分别包含各自的信息,列如method_t(简化)
struct method_t {
//…
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
//…
}
其他更多请查看源码,这里就不贴代码了。