聊聊NSCache

作者: Arnold134777 | 来源:发表于2017-06-19 22:38 被阅读650次

打算了解一下NSCache可能要从前一段时间面试讲起,当时面试者问我了解NSCache吗?我的第一印象是:这是什么类,怎么从来没听过,难道他说的是NSURLCache?于是跟他扯了一通h5的离线缓存的实现。面试完回来一查直接傻眼了,因此做一次学习记录吧。

1.NSCache简述

An NSCache object is a mutable collection that stores key-value pairs, similar to an NSDictionary object. The NSCache class provides a programmatic interface to adding and removing objects and setting eviction policies based on the total cost and number of objects in the cache.

  • The NSCache class incorporates various auto-eviction policies, which ensure that a cache doesn’t use too much of the system’s memory. If memory is needed by other applications, these policies remove some items from the cache, minimizing its memory footprint.
  • You can add, remove, and query items in the cache from different threads without having to lock the cache yourself.
  • Unlike an NSMutableDictionary object, a cache does not copy the key objects that are put into it.
  • NSCache是一个类似NSDictionary一个可变的集合。
  • 提供了可设置缓存的数目与内存大小限制的方式。
  • 保证了处理的数据的线程安全性。
  • 缓存使用的key不需要是实现NSCopying的类。
  • 当内存警告时内部自动清理部分缓存数据。

2.NSCache使用

NSCache *cache = [[NSCache alloc] init];
cache.delegate = self;
//cache.countLimit = 50; // 设置缓存数据的数目
//cache.totalCostLimit = 5 * 1024 * 1024; // 设置缓存的数据占用内存大小
    
- (void)start:(id)sender{
    
    for(int i = 0;i < 1000;i++){
        NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"pptx"]];
        
        // 1.缓存数据
        [cache setObject:data forKey:[NSString stringWithFormat:@"image_%d",arc4random()]];
    }
}

#pragma mark - NSCacheDelegate
- (void)cache:(NSCache *)cache willEvictObject:(id)obj{
    NSLog(@"删除缓存数据");
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    NSLog(@"内存警告");
}

执行结果:

2017-06-18 22:51:58.204455 InterView[1113:223367] 删除缓存数据
2017-06-18 22:51:58.204812 InterView[1113:223367] 删除缓存数据
2017-06-18 22:51:58.205198 InterView[1113:223367] 删除缓存数据
2017-06-18 22:51:58.205521 InterView[1113:223367] 删除缓存数据
2017-06-18 22:51:58.205918 InterView[1113:223367] 删除缓存数据
2017-06-18 22:51:58.206216 InterView[1113:223367] 删除缓存数据
2017-06-18 22:52:05.207987 InterView[1113:223367] 内存警告

3.NSCache的使用场景

3.1 AFNetworking(2.X)中UIImageView+AFNetworking的图片缓存

// 缓存的key使用请求的路径
static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) {
    return [[request URL] absoluteString];
}


// 继承NSCache,实现自定义的cache策略
@interface AFImageCache : NSCache <AFImageCache>
@end

@implementation AFImageCache

- (UIImage *)cachedImageForRequest:(NSURLRequest *)request {
    switch ([request cachePolicy]) {
        case NSURLRequestReloadIgnoringCacheData:
        case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
            return nil;
        default:
            break;
    }

    return [self objectForKey:AFImageCacheKeyFromURLRequest(request)];
}

- (void)cacheImage:(UIImage *)image
        forRequest:(NSURLRequest *)request
{
    if (image && request) {
        [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)];
    }
}
@end
AFImageCache具体的使用
+ (id <AFImageCache>)sharedImageCache {
    static AFImageCache *_af_defaultImageCache = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _af_defaultImageCache = [[AFImageCache alloc] init];
        // 收到内存警告直接清理掉缓存
        [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) {
            [_af_defaultImageCache removeAllObjects];
        }];
    });

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: _af_defaultImageCache;
#pragma clang diagnostic pop
}

AF 3.0及以上已经替换了实现的方式(NSMutableDictionary + GCD保证线程安全),有兴趣可以直接自己看一下3.0源码。

3.2 SDWebImage中SDImageCache图片缓存

// 继承NSCache,实现自定义的cache类
@interface AutoPurgeCache : NSCache
@end

@implementation AutoPurgeCache

- (id)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    return self;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];

}

@end

AutoPurgeCache的使用

初始化
// Init the memory cache
_memCache = [[AutoPurgeCache alloc] init];
_memCache.name = fullNamespace;
缓存图片与取缓存图片
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
    return [self.memCache objectForKey:key];
}

- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        return image;
    }

    // Second check the disk cache...
    UIImage *diskImage = [self diskImageForKey:key];
    if (diskImage && self.shouldCacheImagesInMemory) {
    
        // 计算需要缓存的内存空间
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }

    return diskImage;
}

3.3 React Native(0.38)

  • RCTAsyncLocalStorage数据缓存类
  • RCTImageCache图片缓存类

二者都使用到NSCache完成数据的缓存,初始化与使用与上述的AFNetworkingSDWebImage都很类似,基本原理相同此处不做赘述了。

4.遇到问题

NSCachetotalCostLimit设置了为什么没有生效?

demo中的例子我把cache.totalCostLimit = 5 * 1024 * 1024;注释打开,执行发现直到内存警告才开始自动清理数据?尝试了很多次都是一样的结果。那设置的5M的最大的缓存大小为什么没有起到作用呢?重新查看一下苹果的文档关于totalCostLimit的描述:

Discussion:

If 0, there is no total cost limit. The default value is 0.
When you add an object to the cache, you may pass in a specified cost for the object, such as the size in bytes of the object. If adding this object to the cache causes the cache’s total cost to rise above totalCostLimit, the cache may automatically evict objects until its total cost falls below totalCostLimit. The order in which the cache evicts objects is not guaranteed.
This is not a strict limit, and if the cache goes over the limit, an object in the cache could be evicted instantly, at a later point in time, or possibly never, all depending on the implementation details of the cache.

注意加粗部分,是需要使用如下的接口吗?

- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;

动手尝试将demo中的setObject换成如下实现,发现执行一次就已经触发了自动清理缓存的回调,也基本验证了这一点。

 NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"pptx"]];
[cache setObject:data forKey:[NSString stringWithFormat:@"image_%d",arc4random()] cost:10 * 1024 * 1024];

回头查看AFNetworking以及SDWebImage以及RN中的两处缓存的使用,也充分印证了这一点:设置全局缓存实例时如果设置了totalCostLimit必然存储缓存的方法调用必然带上了cost,否则totalCostLimit是无用的。

相关文章

  • 聊聊NSCache

    打算了解一下NSCache可能要从前一段时间面试讲起,当时面试者问我了解NSCache吗?我的第一印象是:这是什么...

  • 聊聊NSCache

    特点 1.使用方便,类似字典2.线程安全3.内存不足,自动释放存储对象(下面会介绍)4.NSCache的key不会...

  • NSCache内存缓存

    NSCache 基本使用 NSCache缓存类介绍 NSCache源码

  • 正确使用NSCache

    NSCache NSCache是专门用来进行缓存处理的 NSCache简单介绍 1-1.NSCache是苹果官方提...

  • NSCache的介绍

    NSCache是什么 NSCache对象是存储键值对的可变集合,类似于NSDictionary。NSCache类提...

  • 8 缓存模块-内存缓存NSCache、外存缓存 和 缓存策略

    NSCache NSCache是一个类似NSDictionary的可变集合。 NSCache中有一个方法在缓存中设...

  • 了解NSCache的基本使用

    NSCache是专门用来进行缓存处理的, NSCache简单介绍:NSCache是苹果官方提供的缓存类,具体使用和...

  • iOS之NSCache的简单介绍

    NSCache简单说明 NSCache属性和方法介绍 代码示例

  • iOS中NSCache与NSURLCache理解和使用

    参考原文NSCache详解 首先说明 两个类没有任何关系! NSCache基本信息 NSCache是Foundat...

  • NScache缓存类

    01.NSCache是专门用来进行缓存处理的, 02.NSCache简单介绍:2-1 NSCache是苹果官方提供...

网友评论

    本文标题:聊聊NSCache

    本文链接:https://www.haomeiwen.com/subject/fwbnqxtx.html