美文网首页
Runtime源码理解cache_t(方法缓存)

Runtime源码理解cache_t(方法缓存)

作者: 风雨彩虹_123 | 来源:发表于2021-09-27 09:36 被阅读0次

    Class内部结构中有个方法缓存(catch_t),用散列表来缓存曾经调用过的方法,可以提高方法的查找速度。

    struct objc_class : objc_object {
       Class isa;
        Class superclass;
        cache_t cache;             // 方法缓存
        class_data_bits_t bits;    // 使用共用体存储类的信息  &FAST_DATA_MASK 获得class_rw_t结构
    }
    

    class_ro_t和class_rw_t的区别

    class_ro_t存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,里面是没有分类的方法的,是只读的不能修改。

    struct class_ro_t {
        method_list_t * baseMethodList; //方法列表(一维数组)
        protocol_list_t * baseProtocols;//协议列表(一维数组)
        const ivar_list_t * ivars; //成员变量列表
        property_list_t *baseProperties; // 属性列表(一维数组)
    };
    

    class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其,所以可以说class_rw_t是class_ro_t的超集。

    struct class_rw_t {
    //指向只读的结构体,存放类初始信息
       const class_ro_t *ro;
    
    /*
    这三个都是二维数组,是可读可写的,包含了类的初始信息、分类的信息
    methods数组中存储着method_list_t 数组
    method_list_t数组 中存储着method_t
    runtime会将class_ro_t 中的类初始信息合并到这三个数组中
    */
        method_array_t methods; //方法列表(二维数组)
        property_array_t properties;;//属性列表(二维数组)
        protocol_array_t protocols;// 协议列表(二维数组)
    }
    

    method_t是对方法/函数的封装。

    struct method_t {
        SEL name; //函数名   不同类中相同名字的方法,所对应的方法选择器是相同的。
        const char *types; //编码(包括返回值类型,参数类型)
        IMP imp; //指向函数的指针
    };
    

    cache_t 方法缓存

    struct cache_t {
        struct bucket_t *_buckets;   //散列表
        mask_t _mask;  //散列表的长度 - 1   作用:和SEL进行&操作,得到散列表的下标,存储方法
        mask_t _occupied; //已经缓存的方法数量   作用: 用于cache_t扩容的判断
    }
    
    struct bucket_t {
        cache_key_t _key;    //SEL做为key
        IMP _imp;  //函数的内存地址
    }
    

    查看缓存
    编写Object的底层C++结构体代码

    #import <Foundation/Foundation.h>
    
    #ifndef ClassInfo_h
    #define ClassInfo_h
    
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL    //用于取出isa所表示class 、meta_class 的地址
    # elif __x86_64__   //模拟器或Mac电脑
    #   define ISA_MASK        0x00007ffffffffff8ULL     
    # endif
    
    #if __LP64__
    typedef uint32_t mask_t;
    #else
    typedef uint16_t mask_t;
    #endif
    typedef uintptr_t cache_key_t;
    
    #if __arm__  ||  __x86_64__  ||  __i386__
    #define CACHE_END_MARKER 1
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return (i+1) & mask;
    }
    
    #elif __arm64__
    #define CACHE_END_MARKER 0
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return i ? i-1 : mask;
    }
    
    #else
    #error unknown architecture
    #endif
    
    //散列表中缓存的方法信息
    struct bucket_t {
        cache_key_t _key;
        IMP _imp;
    };
    
    //缓存方法的底层结构
    struct cache_t {
        bucket_t *_buckets;
        mask_t _mask;
        mask_t _occupied;
        
        IMP imp(SEL selector)
        {
            mask_t begin = _mask & (long long)selector;
            mask_t i = begin;
            do {
                if (_buckets[i]._key == 0  ||  _buckets[i]._key == (long long)selector) {
                    return _buckets[i]._imp;
                }
            } while ((i = cache_next(i, _mask)) != begin);
            return NULL;
        }
    };
    
    struct entsize_list_tt {
        uint32_t entsizeAndFlags;
        uint32_t count;
    };
    
    // 方法的底层结构
    struct method_t {
        SEL name;
        const char *types;
        IMP imp;
    };
    
    struct method_list_t : entsize_list_tt {
        method_t first;
    };
    
    struct ivar_t {
        int32_t *offset;
        const char *name;
        const char *type;
        uint32_t alignment_raw;
        uint32_t size;
    };
    
    struct ivar_list_t : entsize_list_tt {
        ivar_t first;
    };
    
    struct property_t {
        const char *name;
        const char *attributes;
    };
    
    struct property_list_t : entsize_list_tt {
        property_t first;
    };
    
    struct chained_property_list {
        chained_property_list *next;
        uint32_t count;
        property_t list[0];
    };
    
    typedef uintptr_t protocol_ref_t;
    struct protocol_list_t {
        uintptr_t count;
        protocol_ref_t list[0];
    };
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;  // instance对象占用的内存空间
    #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;
    };
    
    struct class_rw_t {
        uint32_t flags;
        uint32_t version;
        const class_ro_t *ro;
        method_list_t * methods;    // 方法列表
        property_list_t *properties;    // 属性列表
        const protocol_list_t * protocols;  // 协议列表
        Class firstSubclass;
        Class nextSiblingClass;
        char *demangledName;
    };
    
    #define FAST_DATA_MASK          0x00007ffffffffff8UL
    struct class_data_bits_t {
        uintptr_t bits;
    public:
        class_rw_t* data() {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
    };
    
    /* OC对象 */
    struct lc_objc_object {
        void *isa;
    };
    
    /* 类对象 */
    struct lc_objc_class : lc_objc_object {
        Class superclass;
        cache_t cache;
        class_data_bits_t bits;
    public:
        class_rw_t* data() {
            return bits.data();
        }
        
        lc_objc_class* metaClass() {
            return (lc_objc_class *)((long long)isa & ISA_MASK);
        }
    };
    
    #endif /* ClassInfo_h */
    
    

    创建项目并把Object的C++代码导入项目。

    #import <Foundation/Foundation.h>
    #import "ClassInfo.h"
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *person = [[Person alloc] init];
           lc_objc_class *personClass = (__bridge lc_objc_class *)[Person class];
    
            [person personTest];
             cache_t cache = personClass->cache;
            bucket_t *buckets = cache._buckets;
            NSLog(@"%d  %d",cache._mask,cache._occupied);
            for (int i = 0; i <= cache._mask; i++) {
                bucket_t bucket = buckets[i];
                  NSLog(@"%s %p", bucket._key, bucket._imp);
            }
            NSLog(@"-----------");
        }
        return 0;
    }
    打印如下
    2021-09-26 20:27:15.416361+0800 Interview01-cache[35703:1646916] init 0x7ffe2041bc45
    2021-09-26 20:27:29.823632+0800 Interview01-cache[35703:1646916] personTest 0x7e78
    

    在控制台中就可以看到缓存列表中缓存的方法,在调用personTest前,方法缓存列表中只有init方法,在调用personTest后,personTest方法就会存储到方法列表中,方便下次快速调用。

    相关文章

      网友评论

          本文标题:Runtime源码理解cache_t(方法缓存)

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