美文网首页
OC class结构浅析

OC class结构浅析

作者: 番茄炒西红柿啊 | 来源:发表于2020-10-21 18:15 被阅读0次

    前言

    深入理解OC对象一文中有提到类的结构, 本文将进一步深入解析其结构。

    大纲

    • 结构总览
    • 方法 method_t
    • cache_t

    1. 结构总览

    由源码typedef struct objc_class *Class;可知class就是个结构体指针,去看objc_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
        class_rw_t *data() const {
            return bits.data();
        }
    }
    
    类型 名称 解释
    cache_t cache 方法缓存
    class_data_bits_t bits 存储信息

    isasuperclass深入理解OC对象中已经提及,这里不再赘述。类中的相关信息存放在bits,不过需要通过一次位运算来获取。


    返回的是一个class_rw_t类型,源码如下(截取重要部分):
    struct class_rw_t {
        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;
            }
            return v.get<const class_ro_t *>();
        }
        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 *>()->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>()->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 *>()->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>()->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 *>()->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
            }
        }
    }
    
    class_ro_t ro 类的相关信息
    method_list_t methods 方法列表(二维数组)
    property_list_t properties 属性列表(二维数组)
    protocols_list_t protocols 协议列表(二维数组)

    methodspropertiesprotocols分别存储了这个类的相关信息,它们都是可变的。还有个只读的ro也存储了一些信息。

    struct class_ro_t {
        const char * name;
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;
        property_list_t *baseProperties;
        ......
    }
    
    综上所述,可以用下面一张图概括: 结构总览图

    2. method_t

    struct class_rw_t中的methods存储的是这个类的方法

    types采用的是@encode指令表示的字符串编码。具体可参考官方文档或者网上博客

    3. cache_t

    每次调用同样的方法的时候如果都去相关的类中查找,无疑是效率低下的。所以,可以将调用过的方法存储在缓存中,下一次需要调用的时候直接去缓存中查找。struct objc_class 中的cache_t cache就是方法缓存。其存储结构为散列表



    这里大致讲一下流程:
    [object instanceMethod];
    
    1. 当第一次调用instanceMethod时,将@selector(instanceMethod) & _mask会得出一个下标index
    2. 则_buckets[index]中存储的就是该方法的bucket_t
    3. 当第二次调用instanceMethod时,同理将@selector(instanceMethod) & _mask会得出一个下标index.
    4. 由上面可知步骤一步骤三得出的下标index是一样的。
    5. 取出_buckets[index]就可以找到该方法的相关信息了。

    补充一:当然通过将SEL与上_mask肯定会出现:SEL不同,但是计算出来的下标相同的情况。这里apple采用的做法是下标-1的做法。

    #if __arm__  ||  __x86_64__  ||  __i386__
    // objc_msgSend has few registers available.
    // Cache scan increments and wraps at special end-marking bucket.
    #define CACHE_END_MARKER 1
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return (i+1) & mask;
    }
    #elif __arm64__
    // objc_msgSend has lots of registers available.
    // Cache scan decrements. No end marker needed.
    #define CACHE_END_MARKER 0
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return i ? i-1 : mask;
    }
    

    假设通过&运算计算出的下标为index,如果index已经缓存了别的方法,则index -= 1。如果此时index仍然缓存了别的方法,继续index-=1,一次类推。如果index=0还是没有地方缓存,则index=最大值-1。继续一次类推,知道找到为止。读取方法时也是同样逻辑。

    补充二:如果缓存散列表已经满了,则会重新开辟更大的空间来存储,则之前的缓存将会被清空掉。

    相关文章

      网友评论

          本文标题:OC class结构浅析

          本文链接:https://www.haomeiwen.com/subject/dadwpktx.html