YYMemoryCache类的主要结构
@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对象的时间戳属性,并且把该对象放到链表的最前面,从而调整了缓存中对象的顺序,控制了内存占用.