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;
    };
    //…
}

其他更多请查看源码,这里就不贴代码了。