内存缓存比较
NSCache
官方提供的系统类,用法类似于字典,是一个线程安全的类,对对象是用了强引用,系统会自动的做内存管理,缺点是没法对内存中的清除策略做一定的控制。清除是按队列的方式清除 先进先出。
AFnetworikng
其中的AFAutoPurgingImageCache 类是图片缓存,里面涉及到内存缓存。
我们看其中的两个东西,一个是他的缓存的数据结构,一个是清除策略。
通过属性定义及初试化方法,我们可知,他就是一个字典,里面放了一个对象.
1 | - (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { |
通过放入一个缓存的方法,我们可以看到他的清除策略,用的是一个GCD的栏珊函数,做到线程的依赖,来解决字典的线程不安全性,然后通过时间排序,来删除最后一个函数,知道符合内存缓存的大小限制为止。
SDWebImageView
1 | interface SDImageCache () |
从这个定义我们就可以看出 他的内存缓存AutoPurgeCache完全是 用的是封装系统的NSCache,只是加了一个通知.
YYCache
我们再来看YYCache的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@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;
}
@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;
}
可以看出_YYLinkedMap这是一个链表,_YYLinkedMapNode.
首先我们清楚一点,对于内存缓存的去除操作来说,我们要尽可能的快,这样我们性能才能好,而字典的去除操作是0(1),所以不管怎么变化,最终的存储都要落在字典上,但是如果只是字典,对于删除操作来说,我们需要一定的策略去清除内存,而字典是无序的,不管什么策略,肯定不是随机的,肯定是某种意义上的有序,所以,需要我们去在无序的字典中怎么去构建有序的一些东西,在AF中,我们可以看到,他给每个元素一个时间,然后将字典的所有value去排序,这样来构建一个有序,而YYCache就是通过链表的方式来构建有序。
1 | - (void)_trimToCost:(NSUInteger)costLimit { |
就是一个while循环,去清除,他这里元素用的是一个_YYLinkedMap的插入和删除遵循了LRU 队列的规则。我们可以先看清除的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14- (_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;
}
是链表的操作,先看最后一个是不是第一个,是的话 直接置为nil就行,不是的话,将最后一个的前一个_tail->_prev 置为最后一个,然后取出最后一个的后一个_tail->_next.
下面我们看下LRU队列相关代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27- (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;
}
- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
if (_head == node) return;
if (_tail == node) {
_tail = node->_prev;
_tail->_next = nil;
} else {
node->_next->_prev = node->_prev;
node->_prev->_next = node->_next;
}
node->_next = _head;
node->_prev = nil;
_head->_prev = node;
_head = node;
}
当我们访问了一个元素后,就会将这个元素放到lru队列的最前面
