YYMemoryCache原理分析

263 阅读3分钟

YYMemoryCache原理分析

原理简介:YYMemoryCache 是利用用双向链表和 hashMap 完成 LRU 算法设计实现的缓存,并采用头、尾指针控制缓存命中的时间复杂度为O(1)。

LRU: Least Recently Used的简写,是一种页面置换算法,选择最近最久未使用的页面予以淘汰。

详见百度百科:LRU详解

双链表结构图:

代码分析

  • YYLinkedMapNode 节点对象   

 _prev 为一个节点指向前一个节点(前驱结点)的指针,_next为一个节点指向后一个节点(后继节点)的指针。 因为在_dic里已经强引用了,所以在这里需要声明为弱引用。

@interface _YYLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
    id _key;
    id _value;
    NSUInteger _cost;
    NSTimeInterval _time;
}
@end   
  • YYLinkedMap 链表操作

维护缓存命中后双向链表的实现,借此达到LRU算法的目标。

@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic; // do not set object directly
    NSUInteger _totalCost;
    NSUInteger _totalCount;
    _YYLinkedMapNode *_head; // MRU, do not change it directly
    _YYLinkedMapNode *_tail; // LRU, do not change it directly
    BOOL _releaseOnMainThread;
    BOOL _releaseAsynchronously;
}  

YYLinkedMap对象用来管理节点的双向链表, _head、_tai分别l为双向链表中的头、尾指针。

  • 初始化   

初始化实例方法  

- (instancetype)init {
    self = [super init];
    //这里用hashMap(可变字典)来管理节点的数据,保证取值的时间复杂度为O(1)
    _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 
    _releaseOnMainThread = NO;
    _releaseAsynchronously = YES;
    return self;
}
  • 链表头部添加节点   

每次加入节点时,双链表头部修改   

- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    _totalCost += node->_cost;
    _totalCount++;
    if (_head) {
    //链表中不为空,头部插入节点
        node->_next = _head;   // 将新节点后继指针 -> 原来头指针
        _head->_prev = node;  // 原来头节点指针前驱指针 -> 新节点
        _head = node;             //跟新头指针为新添加的节点
    } else {
    //链表中为空,则将头指针和尾指针同时指向新加入的节点
        _head = _tail = node;
    }
}
  • 移动节点至头部   

双链表内如果命中,则移动节点   

- (void)bringNodeToHead:(_YYLinkedMapNode *)node {   
    if (_head == node) return;             //如果移动的节点node 就是头节点,则退出。
    if (_tail == node) {                   //移动节点尾节点,将倒数第二个节点变为尾节点
        _tail = node->_prev;               //原来尾指针 -> node 的前一个节点
        _tail->_next = nil;                //再将前一个节点指针赋值为空  
    } else {                               // 移动节点在链表中间,实现中间节点断链,取出操作
        node->_next->_prev = node->_prev;  //node的后继节点前驱指针 -> node的前驱节点的后继指针
        node->_prev->_next = node->_next;  //node的前驱节点的后继指针 -> node的后继节点
    }
    //node 更新为head 头结点的操作
    node->_next = _head;                   // node 的后继指针 -> 原来的头指针
    node->_prev = nil;                     // node 的前驱指针赋值为空 
    _head->_prev = node;                   // 原来的头节点的前驱指针 -> node 
    _head = node;                          // 更新头指针指向节点,指向现有的node节点
}
  • 删除节点   

双链表中删除节点   

- (void)removeNode:(_YYLinkedMapNode *)node {
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));   //key-value 中移除键值对 
    _totalCost -= node->_cost;                                 
    _totalCount--;                                            // 节点总数 -1 
    if (node->_next) node->_next->_prev = node->_prev;        // 如果node 不为尾节点,node 后继节点的前驱指针-> node 前驱节点
    if (node->_prev) node->_prev->_next = node->_next;        // 如果node 不为头节点,node 前驱节点的后继指针 -> node 后继节点
    if (_head == node) _head = node->_next;                   // 如果node 为头节点,头指针 -> node 的后继节点,(头指针移动到原来第二个节点处)
    if (_tail == node) _tail = node->_prev;                   // 如果node 为尾节点,尾指针 -> node 的前驱节点,(尾指针移动原来的倒数第二个节点)
}   
  • 移除尾节点   

双链表尾部删除节点   

- (_YYLinkedMapNode *)removeTailNode {
    if (!_tail) return nil;                        //判断尾节点是否为空(是否为空表)
    _YYLinkedMapNode *tail = _tail;               // 窗帘一个节点指向尾指针
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));   //删除尾节点key-value 
    _totalCost -= _tail->_cost;                   
    _totalCount--;                                // 总数 -1
    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) {
        CFMutableDictionaryRef holder = _dic;
        _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);
        }
    }
}