美文网首页
『ios』cache_t 源码分析(结构,fill,find,c

『ios』cache_t 源码分析(结构,fill,find,c

作者: butterflyer | 来源:发表于2021-07-14 15:40 被阅读0次

cache_t是什么?看名字就知道缓存。在类结构体中

struct my_objc_class : my_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    my_objc_class* metaClass() {
        return (my_objc_class *)((long long)isa & ISA_MASK);
    }
};

那么他的结构是什么?继续往下看

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

struct bucket_t {
    cache_key_t _key;//SEL作为key
    IMP _imp;//函数的内存地址
};

struct cache_t {
    bucket_t *_buckets; //散列表
    mask_t _mask;//散列表长度 -1
    mask_t _occupied;//已缓存的方法数量
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));
};

mask_t cache_t::mask()
{
    return _mask;
}

mask_t cache_t::capacity()
{
    return mask() ? mask()+1 : 0;
}

通过查阅源码,可以看到cache_t的fill流程

void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
    mutex_locker_t lock(cacheUpdateLock);
    cache_fill_nolock(cls, sel, imp, receiver);
#else
    _collecting_in_critical();
    return;
#endif
}

static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
    cacheUpdateLock.assertLocked();

    // Never cache before +initialize is done
    if (!cls->isInitialized()) return;

    // Make sure the entry wasn't added to the cache by some other thread 
    // before we grabbed the cacheUpdateLock.
    if (cache_getImp(cls, sel)) return;

    cache_t *cache = getCache(cls);
    cache_key_t key = getKey(sel);

    // Use the cache as-is if it is less than 3/4 full
    mask_t newOccupied = cache->occupied() + 1;
    mask_t capacity = cache->capacity();
    if (cache->isConstantEmptyCache()) {//如果没有创建过cache空间
        // Cache is read-only. Replace it.
        cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);
    }
    else if (newOccupied <= capacity / 4 * 3) {//如果空间小于3/4,什么都不做
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {//扩大空间
        // Cache is too full. Expand it.
        cache->expand();
    }

    // Scan for the first unused slot and insert there.
    // There is guaranteed to be an empty slot because the 
    // minimum size is 4 and we resized at 3/4 full.
    bucket_t *bucket = cache->find(key, receiver);
    if (bucket->key() == 0) cache->incrementOccupied();
    bucket->set(key, imp);
}

//空间扩展
void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
    
    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);
}
//可以看出cache_key_t就是SEL
cache_key_t getKey(SEL sel) 
{
    assert(sel);
    return (cache_key_t)sel;
}


//存储 key为sel  value 为IMP
void bucket_t::set(cache_key_t newKey, IMP newImp)
{
    assert(_key == 0  ||  _key == newKey);

    // objc_msgSend uses key and imp with no locks.
    // It is safe for objc_msgSend to see new imp but NULL key
    // (It will get a cache miss but not dispatch to the wrong place.)
    // It is unsafe for objc_msgSend to see old imp and new key.
    // Therefore we write new imp, wait a lot, then write new key.
    
    _imp = newImp;
    
    if (_key != newKey) {
        mega_barrier();
        _key = newKey;
    }
}


可以继续分析源码,来看find方法

bucket_t * cache_t::find(cache_key_t k, id receiver)
{
    assert(k != 0);

    bucket_t *b = buckets();//创建散列表
    mask_t m = mask();//
    mask_t begin = cache_hash(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);
}
//通过位运算来返回对应的mask_t
static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}

下图可以看出cache的查找逻辑


image.png

cache_t 清除操作

void cache_erase_nolock(Class cls)
{
    cacheUpdateLock.assertLocked();

    cache_t *cache = getCache(cls);

    mask_t capacity = cache->capacity();
    if (capacity > 0  &&  cache->occupied() > 0) {
        auto oldBuckets = cache->buckets();
        auto buckets = emptyBucketsForCapacity(capacity);
      //从这里可以知道mask的长度为buckets的长度-1
        cache->setBucketsAndMask(buckets, capacity - 1); // also clears occupied

        cache_collect_free(oldBuckets, capacity);
        cache_collect(false);
    }
}

void cache_delete(Class cls)
{
    mutex_locker_t lock(cacheUpdateLock);
    if (cls->cache.canBeFreed()) {
        if (PrintCaches) recordDeadCache(cls->cache.capacity());
        free(cls->cache.buckets());
    }
}

void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
{
    // objc_msgSend uses mask and buckets with no locks.
    // It is safe for objc_msgSend to see new buckets but old mask.
    // (It will get a cache miss but not overrun the buckets' bounds).
    // It is unsafe for objc_msgSend to see old buckets and new mask.
    // Therefore we write new buckets, wait a lot, then write new mask.
    // objc_msgSend reads mask first, then buckets.

    // ensure other threads see buckets contents before buckets pointer
    mega_barrier();

    _buckets = newBuckets;
    
    // ensure other threads see new buckets before new mask
    mega_barrier();
    
    _mask = newMask;
    _occupied = 0;
}

static void flushCaches(Class cls)
{
    runtimeLock.assertWriting();

    mutex_locker_t lock(cacheUpdateLock);

    if (cls) {
        foreach_realized_class_and_subclass(cls, ^(Class c){
            cache_erase_nolock(c);
        });
    }
    else {
        foreach_realized_class_and_metaclass(^(Class c){
            cache_erase_nolock(c);
        });
    }
}

runtime源码中,调用cache_erase_nolock 的地方
1.attachCategories(Class cls, category_list *cats, bool flush_caches)
2._method_setImplementation
3.method_exchangeImplementations
4.addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
5.setSuperclass

runtime源码中,调用cache_delete的地方
free_class(Class cls)

cache_t用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度

相关文章

网友评论

      本文标题:『ios』cache_t 源码分析(结构,fill,find,c

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