美文网首页
Runtime 02 - Class(方法信息列表、方法缓存)

Runtime 02 - Class(方法信息列表、方法缓存)

作者: 石头89 | 来源:发表于2019-08-07 23:56 被阅读0次

    Runtime 02 - Class(方法信息列表、方法缓存)

    Class 结构图

    关于对 objc_class、class_data_bits_t、class_rw_t、class_ro_t 的解读,请参考这里Objective-C 对象的本质 01 - 底层实现)。

    SEL、IMP

    // 方法名(选择器)
    //
    // 可以通过 @selector() 或 sel_registerName() 获取 SEL。
    // 可以通过 sel_getName() 或 NSStringFromSelector() 将 SEL 转换为字符串。
    // 不同类中的同名方法所对应的 SEL 是相同的。
    //
    /// An opaque type that represents a method selector.
    typedef struct objc_selector *SEL;
    
    // 函数指针
    //
    /// A pointer to the function of a method implementation. 
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
    typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
    #endif
    

    bucket_t、cache_t

    typedef unsigned long uintptr_t;
    
    // 方法缓存中的 Key
    #if __LP64__
    typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
    #else
    typedef uint16_t mask_t;
    #endif
    typedef uintptr_t cache_key_t;
    
    // 方法缓存
    struct bucket_t {
    private:
        cache_key_t _key; // key 是方法的 SEL
        IMP _imp;         // 函数指针
    
    public:
        inline cache_key_t key() const { return _key; }
        inline IMP imp() const { return (IMP)_imp; }
        inline void setKey(cache_key_t newKey) { _key = newKey; }
        inline void setImp(IMP newImp) { _imp = newImp; }
    
        void set(cache_key_t newKey, IMP newImp);
    };
    
    // 方法缓存列表
    // 
    // 用散列表(哈希表)来缓存调用多的方法,可以提高查找方法的速度。
    // 
    // _mask 为什么是 _buckets 的长度 -1 ?
    // -1 能确保 (key & _mask) <= (_buckets 的长度 - 1),用来作为 _buckets 的下标来取值。
    // 在 cache_t::find() 函数中可以看到计算下标的代码。
    // 
    // _buckets 的动态扩容:
    // 向 _buckets 添加最后一个缓存时会进行扩容。
    // 每次扩容为原来的两倍(2^n)。
    // 扩容时会将缓存清除,因为扩容后 _mask 发生了变化。
    // 通过 cache_t::expand() 函数中进行扩容。
    struct cache_t {
        struct bucket_t *_buckets; // 散列表
        mask_t _mask;              // 散列表的长度 -1
        mask_t _occupied;          // 已经缓存的方法数量(<= _mask)
    
    public:
        struct bucket_t *buckets();
        mask_t mask();
        mask_t occupied();
        void incrementOccupied();
        void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
        void initializeToEmpty();
    
        mask_t capacity();
        bool isConstantEmptyCache();
        bool canBeFreed();
    
        static size_t bytesForCapacity(uint32_t cap);
        static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
    
        void expand();
        void reallocate(mask_t oldCapacity, mask_t newCapacity);
        struct bucket_t * find(cache_key_t key, id receiver);
    
        static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
    };
    
    /* Initial cache bucket count. INIT_CACHE_SIZE must be a power of two. */
    enum {
        INIT_CACHE_SIZE_LOG2 = 2,
        INIT_CACHE_SIZE      = (1 << INIT_CACHE_SIZE_LOG2)
    };
    
    // 扩容列表
    void cache_t::expand()
    {
        cacheUpdateLock.assertLocked();
        
        // 计算新的长度(oldCapacity * 2),初始值为 4(INIT_CACHE_SIZE)
        uint32_t oldCapacity = capacity();
        uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;
    
        if ((uint32_t)(mask_t)newCapacity != newCapacity) {
            // mask overflow - can't grow further
            // fixme this wastes one bit of mask
            newCapacity = oldCapacity;
        }
    
        // 扩容并清除旧的缓存
        reallocate(oldCapacity, newCapacity);
    }
    
    // 扩容列表并清除旧的缓存
    void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
    {
        bool freeOld = canBeFreed();
    
        bucket_t *oldBuckets = buckets();
        bucket_t *newBuckets = allocateBuckets(newCapacity);
    
        // Cache's old contents are not propagated. 
        // This is thought to save cache memory at the cost of extra cache fills.
        // fixme re-measure this
    
        assert(newCapacity > 0);
        assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
    
        setBucketsAndMask(newBuckets, newCapacity - 1);
        
        if (freeOld) {
            cache_collect_free(oldBuckets, oldCapacity);
            cache_collect(false);
        }
    }
    
    // 从散列表中查找方法缓存
    bucket_t * cache_t::find(cache_key_t k, id receiver)
    {
        assert(k != 0);
    
        bucket_t *b = buckets();         // 取出散列表(_buckets)
        mask_t m = mask();               // 取出散列表的长度 -1 的值(_mask)
        mask_t begin = cache_hash(k, m); // 计算缓存的哈希值(k & m)
        mask_t i = begin;
        do {
            if (b[i].key() == 0  ||  b[i].key() == k) {
                return &b[i];
            }
        } while ((i = cache_next(i, m)) != begin);
    
        // hack
        Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
        cache_t::bad_cache(receiver, (SEL)k, cls);
    }
    
    // 根据当前下标(i)计算下一个下标(arm64 架构中是 i - 1 或 mask)
    
    #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;
    }
    
    #else
    #error unknown architecture
    #endif
    

    method_t、method_list_t、method_array_t

    // 方法信息
    // 
    // *types 示意:| 返回值 | 参数1 | 参数2 | ... | 参数n |
    // 例如一个没有返回值且没有参数的方法,它的 method_t 中的 *types 为 v16@:8
    // | v16  | @0 | :8  |
    // | void | id | SEL |
    // 两个参数为默认参数,id 对应 self,SEL 对应 _cmd
    // 
    // *types 中的数字:
    // 第一个数字:所有参数一共占用多少字节
    // 后面的数字:当前参数从第几个字节开始
    struct method_t {
        SEL name;          // 方法名
        const char *types; // 包含了返回值类型、参数类型的编码字符串
        IMP imp;           // 函数指针
    };
    
    // 方法信息列表(一维数组)
    // Two bits of entsize are used for fixup markers.
    struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
        bool isFixedUp() const;
        void setFixedUp();
    
        uint32_t indexOfMethod(const method_t *meth) const {
            uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
            assert(i < count);
            return i;
        }
    };
    
    // 方法信息列表(二维数组)
    class method_array_t : public list_array_tt<method_t, method_list_t> {
        typedef list_array_tt<method_t, method_list_t> Super;
    
     public:
        method_list_t **beginCategoryMethodLists() {
            return beginLists();
        }
        
        method_list_t **endCategoryMethodLists(Class cls);
    
        method_array_t duplicate() {
            return Super::duplicate<method_array_t>();
        }
    };
    

    Type Encoding(通过 @encode() 可以查看指定类型的字符串编码)


    相关文章

      网友评论

          本文标题:Runtime 02 - Class(方法信息列表、方法缓存)

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