YYCache源码分析(一)
来源:互联网 发布:美图软件 编辑:程序博客网 时间:2024/05/29 16:31
一. 文件结构:
YYCache主要分为YYCache、YYDiskCache和YYMemoryCache三个类,YYDiskCache实现了硬盘缓存的功能,YYMemoryCache实现了内存缓存的功能,YYCache类提供了通用的缓存存取的方法,内部调用YYMemoryCache和YYDiskCache的方法。
二. YYCache的使用:
// 0.初始化YYCache YYCache *cache = [YYCache cacheWithName:@"mydb"]; // 1.缓存普通字符 [cache setObject:@"哈哈哈" forKey:@"name"]; NSString *name = (NSString *)[cache objectForKey:@"name"]; NSLog(@"name: %@", name); // 2.缓存模型 [cache setObject:(id<NSCoding>)model forKey:@"user"]; // 3.缓存数组 NSMutableArray *array = @[].mutableCopy; for (NSInteger i = 0; i < 10; i ++) { [array addObject:model]; } // 异步缓存 [cache setObject:array forKey:@"user" withBlock:^{ // 异步回调 NSLog(@"%@", [NSThread currentThread]); NSLog(@"array缓存完成...."); }]; // 延时读取 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 异步读取 [cache objectForKey:@"user" withBlock:^(NSString * _Nonnull key, id<NSCoding> _Nonnull object) { // 异步回调 NSLog(@"%@", [NSThread currentThread]); NSLog(@"%@", object); }]; });
三. YYMemoryCache.h文件分析
YYMemoryCache是内存缓存,所以存取速度非常快,主要用到两种数据结构的LRU淘汰算法
1.LRU
Cache的容量是有限的,当Cache的空间都被占满后,如果再次发生缓存失效,就必须选择一个缓存块来替换掉.LRU法是依据各块使用的情况, 总是选择那个最长时间未被使用的块替换。这种方法比较好地反映了程序局部性规律
2.LRU主要采用两种数据结构实现
- 双向链表(Doubly Linked List)
- 哈希表(Dictionary)
3.对一个Cache的操作无非三种:插入、替换、查找
- 插入:当Cache未满时,新的数据项只需插到双链表头部即可
- 替换:当Cache已满时,将新的数据项插到双链表头部,并删除双链表的尾结点即可
- 查找:每次数据项被查询到时,都将此数据项移动到链表头部
4.分析图(分析源码时可以对照该图)
5.YYMemoryCache.m里的两个类
链表节点_YYLinkedMapNode
:
@interface _YYLinkedMapNode : NSObject { @package // 指向前一个节点 __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic // 指向后一个节点 __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic // 缓存key id _key; // 缓存对象 id _value; // 当前缓存内存开销 NSUInteger _cost; // 缓存时间 NSTimeInterval _time;}@end
链表_YYLinkedMap
:
@interface _YYLinkedMap : NSObject { @package // 用字典保存所有节点_YYLinkedMapNode (为什么不用oc字典?因为用CFMutableDictionaryRef效率高,毕竟基于c) CFMutableDictionaryRef _dic; // 总缓存开销 NSUInteger _totalCost; // 总缓存数量 NSUInteger _totalCount; // 链表头节点 _YYLinkedMapNode *_head; // 链表尾节点 _YYLinkedMapNode *_tail; // 是否在主线程上,异步释放 _YYLinkedMapNode对象 BOOL _releaseOnMainThread; // 是否异步释放 _YYLinkedMapNode对象 BOOL _releaseAsynchronously;}// 添加节点到链表头节点- (void)insertNodeAtHead:(_YYLinkedMapNode *)node;// 移动当前节点到链表头节点- (void)bringNodeToHead:(_YYLinkedMapNode *)node;// 移除链表节点- (void)removeNode:(_YYLinkedMapNode *)node;// 移除链表尾节点(如果存在)- (_YYLinkedMapNode *)removeTailNode;// 移除所有缓存- (void)removeAll;@end
方法插入、替换、查找方法实现:
// 添加节点到链表头节点- (void)insertNodeAtHead:(_YYLinkedMapNode *)node { // 字典保存链表节点node CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node)); // 叠加该缓存开销到总内存开销 _totalCost += node->_cost; // 总缓存数+1 _totalCount++; if (_head) { // 存在链表头,取代当前表头 node->_next = _head; _head->_prev = node; // 重新赋值链表表头临时变量_head _head = node; } else { // 不存在链表头 _head = _tail = node; }}
存在表头情况图形分析
// 移动当前节点到链表头节点- (void)bringNodeToHead:(_YYLinkedMapNode *)node { // 当前节点已是链表头节点 if (_head == node) return;if (_tail == node) { //**如果node是链表尾节点** // 把node指向的上一个节点赋值给链表尾节点 _tail = node->_prev; // 把链表尾节点指向的下一个节点赋值nil _tail->_next = nil; } else { //**如果node是非链表尾节点和链表头节点** // 把node指向的上一个节点赋值給node指向的下一个节点node指向的上一个节点 node->_next->_prev = node->_prev; // 把node指向的下一个节点赋值给node指向的上一个节点node指向的下一个节点 node->_prev->_next = node->_next; } // 把链表头节点赋值给node指向的下一个节点 node->_next = _head; // 把node指向的上一个节点赋值nil node->_prev = nil; // 把节点赋值给链表头节点的指向的上一个节点 _head->_prev = node; _head = node;}
如果node是非链表尾节点和链表头节点情况图形分析
// 移除节点- (void)removeNode:(_YYLinkedMapNode *)node { // 从字典中移除node CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key)); // 减掉总内存消耗 _totalCost -= node->_cost; // // 总缓存数-1 _totalCount--; // 重新连接链表 if (node->_next) node->_next->_prev = node->_prev; if (node->_prev) node->_prev->_next = node->_next; if (_head == node) _head = node->_next; 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; // 总缓存数-1 _totalCount--; if (_head == _tail) { // 清除节点,链表上已无节点了 _head = _tail = nil; } else { // 设倒数第二个节点为链表尾节点 _tail = _tail->_prev; _tail->_next = nil; } // 返回完tail后_tail将会释放 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); } }}
YYMemoryCache.m实现分析,增删改都是调用上面的方法,下面分析查找与添加缓存方法实现
// 查找缓存- (id)objectForKey:(id)key { if (!key) return nil; // 加锁,防止资源竞争 // 互斥锁。 pthread_mutex_lock(&_lock); // _lru为链表_YYLinkedMap,全部节点存在_lru->_dic中 // 获取节点 _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key)); if (node) { //** 有对应缓存 ** // 重新更新缓存时间 node->_time = CACurrentMediaTime(); // 把当前node移到链表表头(为什么移到表头?根据LRU淘汰算法:Cache的容量是有限的,当Cache的空间都被占满后,如果再次发生缓存失效,就必须选择一个缓存块来替换掉.LRU法是依据各块使用的情况, 总是选择那个最长时间未被使用的块替换。这种方法比较好地反映了程序局部性规律) [_lru bringNodeToHead:node]; } // 解锁 pthread_mutex_unlock(&_lock); // 有缓存则返回缓存值 return node ? node->_value : nil;}// 添加缓存- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost { if (!key) return; if (!object) { // ** 缓存对象为空,移除缓存 ** [self removeObjectForKey:key]; return; } // 加锁 pthread_mutex_lock(&_lock); // 查找缓存 _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]; } if (_lru->_totalCost > _costLimit) { // ** 总缓存开销大于设定的开销 ** // 异步清理最久未使用的缓存 dispatch_async(_queue, ^{ [self trimToCost:_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]; // 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);}
YYMemoryCache分析先到这里,接下来还会继续理解和阅读YY大神的更多源码。进行YYMemoryCache源码分析时,也看了很多人的关于YYMemoryCache源码分析的分享,感谢大哥们的分享。
阅读全文
0 0
- YYCache源码分析(一)
- YYCache源码分析(二)
- YYCache源码分析(三)
- iOS源码解析—YYCache(概述)
- 源码解析--YYCache
- 源码分析(一)
- iOS源码解析—YYCache(YYMemoryCache)
- iOS源码解析—YYCache(YYDiskCache)
- JUnit源码分析(一)
- osworkflow源码分析(一)
- Log4net源码分析(一)
- Mangos源码分析(一)
- Notepad++源码分析(一)
- Log4net源码分析(一)
- ConcurrentHashMap 源码分析 (一)
- rabbitmq源码分析(一)
- rabbitmq源码分析(一)
- Notepad++源码分析(一)
- classify_video.py学习记录
- 死锁机制以及生产者与消费者模式
- Datatables的各类参数
- BZOJ2257 瓶子和燃料
- 微信api ----统一下单
- YYCache源码分析(一)
- linux统计某一些文件的大小总和
- 网络流--求最大流:EK算法
- 自定义异常的简单使用
- Histogram intersection(直方图交叉核,Pyramid Match Kernel)
- tensorflow
- linux新建ftp用户 待完善
- Spring注解@Resource和@Autowired区别对比
- java.text.ParseException: Unparseable date: "2016-11-02 00-02-00"