YYMemoryCache源码浅析

350 阅读3分钟

YYMemoryCache类的主要结构

YYMemoryCache.png

@implementation YYMemoryCache {
    pthread_mutex_t _lock; // 互斥锁,用来实现线程安全
    _YYLinkedMap *_lru; // 最近最少使用算法实现 
    dispatch_queue_t _queue; // 串行队列
}

LRU淘汰算法

  • YYMemeryCache中使用了LRU规则来进行缓存的淘汰,当发生内存警告或者缓存值达到上限,会优先淘汰哪些时间戳靠前的对象,保障合理的内存使用。YYMemoryCache中有两个重要的内部对象_YYLinkedMap,_YYLinkedMapNode,以双向链表+字典方式实现了LRU
_YYLinkedMapNode
  • 顾名思义,这是双向链表中的节点,被_YYLinkedMap持有,持有了前驱节点和后继结点的指针,并且记录了存储对象的key和value。
@interface _YYLinkedMapNode : NSObject {
@package
__unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic 
__unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
id _key; //锁存对象的key
id _value; //具体存储的对象
NSUInteger _cost; // 所存对象占用空间
NSTimeInterval _time; // 最近一次使用该对象的时间戳
}
_YYLinkedMap
  • _YYLinkedMap是实现LRU的关键,管理所有_YYLinkedMapNode节点的增删排序逻辑,本质还是是使用了CFMutableDictionaryRef这个字典来进行对象的存储,时间复杂度为O(1)主要变量如下:
@interface _YYLinkedMap : NSObject {

    **@package**

    CFMutableDictionaryRef _dic; // 保存对象的字典

    NSUInteger _totalCost;// 总内存

    NSUInteger _totalCount;// 总个数

    _YYLinkedMapNode *_head; // MRU, 指向最近最多使用的头节点

    _YYLinkedMapNode *_tail; // LRU, 指向最近最少的尾结点

    **BOOL** _releaseOnMainThread;

    **BOOL** _releaseAsynchronously;

}

-主要的方法:

// 添加结点:
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    // 将结点node保存在_dic中
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    // 根据结点的cost开销修改map记录的总开销数
    _totalCost += node->_cost;
    // 记录缓存个数
    _totalCount++;
    if (_head) {
        // 将结点node设置为头结点
        node->_next = _head;
        _head->_prev = node;
        _head = node;
    } else {
        // 若是头结点为nil, 说明链表为空, 添加的结点node就是头结点 而且仍是尾结点
        _head = _tail = node;
    }
}
//移动结点:
- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    // 若是只有一个结点, 说明不须要移动
    if (_head == node) return;
    
    if (_tail == node) {
        // 若是node是尾结点, 从新设置尾结点
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        // node既不是头结点又不是尾结点, 至关于删除结点node
        node->_next->_prev = node->_prev;
        node->_prev->_next = node->_next;
    }
    // 将node设置为头结点
    node->_next = _head;
    node->_prev = nil;
    _head->_prev = node;
    _head = node;
}
//删除结点:
- (void)removeNode:(_YYLinkedMapNode *)node {
    // 将结点node从字典_dic中移除, 注意这是node的引用计数减1
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    // 修改总开销
    _totalCost -= node->_cost;
    // 缓存数量减1
    _totalCount--;
    // 修改node结点的下一个结点prev指向
    if (node->_next) node->_next->_prev = node->_prev;
    // 修改node结点的上一个结点的next指向
    if (node->_prev) node->_prev->_next = node->_next;
    // 若是node是头结点, 将头结点设置为node的下一个结点
    if (_head == node) _head = node->_next;
    // 若是node是尾结点, 将尾结点设置为node的上一个结点
    if (_tail == node) _tail = node->_prev;
}
//移除尾结点:
- (_YYLinkedMapNode *)removeTailNode {
    if (!_tail) return nil;
    // 获取尾结点
    _YYLinkedMapNode *tail = _tail;
    // 将尾结点从字典中移除, 由于字典有强引用
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
    _totalCost -= _tail->_cost;
    _totalCount--;
    if (_head == _tail) {
        // 若是头结点=尾结点, 移除以后链表为空
        _head = _tail = nil;
    } else {
        // 否者重置尾结点
        _tail = _tail->_prev;
        _tail->_next = nil;
    }
    return tail;
}	
//清空数据:
- (void)removeAll {
    // 清空信息
    _totalCost = 0;
    _totalCount = 0;
    _head = nil;
    _tail = nil;
    if (CFDictionaryGetCount(_dic) > 0) {
        // 至关于对_dic进行了一次mutableCopy, 因为_dic是不可变, 因此holder和_dic执行了同一块内存空间(堆空间)
        CFMutableDictionaryRef holder = _dic;
        // 从新在堆空间申请内存, _dic执行新分配的内存(以前堆空间的内存地址保存在holder中)
        _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        // 若是须要异步执行, 不阻塞当前线程
        if (_releaseAsynchronously) {
            dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else if (_releaseOnMainThread && !pthread_main_np()) {
            // 主线程执行
            dispatch_async(dispatch_get_main_queue(), ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else {
            // 主线程执行
            CFRelease(holder);
        }
    }
}
  • 在每次memoryCache调用setObject:forkey:或者objectForKey:方法的时候,都会更新对应的linkedMapNode对象的时间戳属性,并且把该对象放到链表的最前面,从而调整了缓存中对象的顺序,控制了内存占用.