iOS NSCache 缓存策略

1.概念:NSCache缓存策略中主要使用的是_GSCachedObject类,下图是_GSCachedObject的源码,定义中重点的分别是缓存的访问次数,缓存当前消耗的大小,是否能够被清除的标记

iOS NSCache 缓存策略_第1张图片

 

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.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.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()

    }

iOS NSCache 缓存策略_第2张图片

通过insert函数可以看出,通过cost排序,在外部会优先删除占用内存小的缓存对象

swift缓存策略:

1.通过totalCostLimit所有的消耗大小限制和当前总消耗大小做比对,大于零进行while循环移除entry缓存对象

2.通过countLimit缓存数量限制和当前缓存对象的数量大小做差值,大于零进行while循环移除entry缓存对象

你可能感兴趣的:(ios)