1.概念:NSCache缓存策略中主要使用的是_GSCachedObject类,下图是_GSCachedObject的源码,定义中重点的分别是缓存的访问次数,缓存当前消耗的大小,是否能够被清除的标记
2.核心缓存策略源码
- (void)_evictObjectsToMakeSpaceForObjectWithCost: (NSUInteger)cost
{
/** 计算需要清除的空间 */
NSUInteger spaceNeeded = 0;
/** 缓存数量 */
NSUInteger count = [_objects count];
if (_costLimit > 0 && _totalCost + cost > _costLimit)
{
/** spaceNeeded:计算需要清除的空间 = _totalCost:总的消耗内存大小+已有的 -_costLimit:限制消耗大小 */
spaceNeeded = _totalCost + cost - _costLimit;
}
// Only evict if we need the space.
if (count > 0 && (spaceNeeded > 0 || count >= _countLimit))
{
NSMutableArray *evictedKeys = nil;
// Round up slightly.
/**
averageAccesses:平均访问次数
_totalAccesses:所有访问次数的总和
count:内存中的缓存对象
几乎淘汰一半的缓存,所以乘0.2
(_totalAccesses / (double)count) * 0.2)可能为0,所以+1
*/
NSUInteger averageAccesses = ((_totalAccesses / (double)count) * 0.2) + 1;
NSEnumerator *e = [_accesses objectEnumerator];
_GSCachedObject *obj;
if (_evictsObjectsWithDiscardedContent)
{
evictedKeys = [[NSMutableArray alloc] init];
}
while (nil != (obj = [e nextObject]))
{
// Don't evict frequently accessed objects.
/** 当前的访问次数 是否小于平均的访问次数 且 当前对象可移除 */
if (obj->accessCount < averageAccesses && obj->isEvictable)
{
/** 发送通知,释放内存 */
[obj->object discardContentIfPossible];
if ([obj->object isContentDiscarded])
{
NSUInteger cost = obj->cost;
// Evicted objects have no cost.
obj->cost = 0;
// Don't try evicting this again in future; it's gone already.
obj->isEvictable = NO;
// Remove this object as well as its contents if required
if (_evictsObjectsWithDiscardedContent)
{
[evictedKeys addObject: obj->key];
}
_totalCost -= cost;
// If we've freed enough space, give up
/** 消耗的 > 需要清除的空间 */
if (cost > spaceNeeded)
{
break;
}
spaceNeeded -= cost;
}
}
}
// Evict all of the objects whose content we have discarded if required
if (_evictsObjectsWithDiscardedContent)
{
NSString *key;
e = [evictedKeys objectEnumerator];
while (nil != (key = [e nextObject]))
{
[self removeObjectForKey: key];
}
}
[evictedKeys release];
}
}
2.淘汰策略:在while循环中通过比对cost > spaceNeeded来进行缓存对象obj的移除
open func setObject(_ obj: ObjectType, forKey key: KeyType, cost g: Int) {
let g = max(g, 0)
let keyRef = NSCacheKey(key)
/** 加锁 */
_lock.lock()
let costDiff: Int
/** 查找缓存列表中是否有key对应的缓存对象,有的话替换 */
/**
_entries:
key:NSCacheKey
value:NSCacheEntry
*/
if let entry = _entries[keyRef] {
costDiff = g - entry.cost
entry.cost = g
entry.value = obj
if costDiff != 0 {
/** 移除旧的缓存 */
remove(entry)
/** 添加新的缓存 */
insert(entry)
}
} else {
let entry = NSCacheEntry(key: key, value: obj, cost: g)
_entries[keyRef] = entry
insert(entry)
costDiff = g
}
_totalCost += costDiff
/** totalCostLimit:所有消耗大小限制 ,_totalCost:当前消耗,*/
var purgeAmount = (totalCostLimit > 0) ? (_totalCost - totalCostLimit) : 0
while purgeAmount > 0 {
/** head:链表头结点 */
if let entry = _head {
/** 回调函数 */
delegate?.cache(unsafeDowncast(self, to:NSCache<AnyObject, AnyObject>.self), willEvictObject: entry.value)
_totalCost -= entry.cost
purgeAmount -= entry.cost
/** 移除缓存对象 */
remove(entry) // _head will be changed to next entry in remove(_:)
/** 表中entry对应的key也置为nil */
_entries[NSCacheKey(entry.key)] = nil
} else {
break
}
}
/** countLimit:缓存数量限制,_entries.count:当前缓存对象的数量 */
var purgeCount = (countLimit > 0) ? (_entries.count - countLimit) : 0
while purgeCount > 0 {
/** head:链表头结点 */
if let entry = _head {
/** 回调函数 */
delegate?.cache(unsafeDowncast(self, to:NSCache<AnyObject, AnyObject>.self), willEvictObject: entry.value)
_totalCost -= entry.cost
/** 每移除一次,缓存数量 - 1 */
purgeCount -= 1
/** 移除缓存对象 */
remove(entry) // _head will be changed to next entry in remove(_:)
/** 表中entry对应的key也置为nil */
_entries[NSCacheKey(entry.key)] = nil
} else {
break
}
}
/** 解锁 */
_lock.unlock()
}
通过insert函数可以看出,通过cost排序,在外部会优先删除占用内存小的缓存对象
swift缓存策略:
1.通过totalCostLimit所有的消耗大小限制和当前总消耗大小做比对,大于零进行while循环移除entry缓存对象
2.通过countLimit缓存数量限制和当前缓存对象的数量大小做差值,大于零进行while循环移除entry缓存对象
网友评论