YYMemoryCache内部通过一个双向循环链表 _YYLinkedMap 来管理数据,并将_YYLinkedMapNode根据key值存放在一个CFMutableDictionaryRef中 ,采用了LRU淘汰算法
核心实现代码如下
添加数据的方法
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
if (!key) return;
if (!object) {
[self removeObjectForKey:key];
return;
}
pthread_mutex_lock(&_lock); ///加锁
///取出存放的node
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
///获取当前时间
NSTimeInterval now = CACurrentMediaTime();
if (node) { ///如果节点存在 就更新数据 和 时间 并把节点移动到链表头部
_lru->_totalCost -= node->_cost;
_lru->_totalCost += cost;
node->_cost = cost;
node->_time = now;
node->_value = object;
[_lru bringNodeToHead:node];
} else { ///如果节点不存在 就创建一个节点 挂到链表中 然后将新节点插入到链表头部
node = [_YYLinkedMapNode new];
node->_cost = cost;
node->_time = now;
node->_key = key;
node->_value = object;
[_lru insertNodeAtHead:node];
}
/// 链表的 _totalCost 是否大于 _costLimit 如果大于就做清除的操作
if (_lru->_totalCost > _costLimit) {
dispatch_async(_queue, ^{
[self trimToCost:self->_costLimit];
});
}
/// 判断链表存放的数量 是否 大于 最大缓存数量 如果大于 就移除一些节点数据
if (_lru->_totalCount > _countLimit) {
/// 移除尾节点数据
_YYLinkedMapNode *node = [_lru removeTailNode];
if (_lru->_releaseAsynchronously) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[node class]; //hold and release in queue
});
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
[node class]; //hold and release in queue
});
}
}
pthread_mutex_unlock(&_lock);
}
数据删除的方法
- (void)removeObjectForKey:(id)key {
if (!key) return;
pthread_mutex_lock(&_lock);
///取出节点数据
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
if (node) { //如果节点存在 就移除这个节点
[_lru removeNode:node];
if (_lru->_releaseAsynchronously) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[node class]; //hold and release in queue
});
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
[node class]; //hold and release in queue
});
}
}
pthread_mutex_unlock(&_lock);
}
查找数据的方法
- (id)objectForKey:(id)key {
if (!key) return nil;
pthread_mutex_lock(&_lock);
/// 获取节点数据 如果不为空 代表这个节点已经在链表内部挂载
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
if (node) { /// 更新访问时间
node->_time = CACurrentMediaTime();
/// 将最新访问数据移动到链表头部
[_lru bringNodeToHead:node];
}
pthread_mutex_unlock(&_lock);
return node ? node->_value : nil;
}
递归调用 每隔5秒钟 去检查缓存情况 如果超出设定的最大缓存数量 就移除链表末尾的数据
- (void)_trimToCount:(NSUInteger)countLimit {
BOOL finish = NO;
pthread_mutex_lock(&_lock);
if (countLimit == 0) { // 如果限定数量为0 就清空链表
[_lru removeAll];
finish = YES;
} else if (_lru->_totalCount <= countLimit) { ///如果没有超出缓存数量 就 标记为finish
finish = YES;
}
pthread_mutex_unlock(&_lock);
if (finish) return;
NSMutableArray *holder = [NSMutableArray new];
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) { /// 尝试加锁 如果超出了最大缓存数据数量 就 移除尾结点数据 直到 缓存数据数量 小于等于 最大缓存数据的限制
if (_lru->_totalCount > countLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
/// 释放内存的一些操作
if (holder.count) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[holder count]; // release in queue
});
}
}
关于双向链表的一些插入 删除 节点 查找节点 不再一一赘述 ,有兴趣同学可以看小编基于LRU算法写的LRUCache 跟YYMemoryCache处理细节基本一样
https://github.com/ZhaoBingDong/LRUCache.git