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);
}
}
}