# 50、构建缓存时选用NSCache而非NSDictionary

&#x20;`NSCache`：

1. 当系统资源将耗尽时，自动删减缓存（若用普通字典，需要自己写挂钩，在“低内存”警告时通知删减缓存）。&#x20;
2. 还会先行删减“最久未使用的”对象。
3. 不会“拷贝”键，而是会“保留”它（当键不支持拷贝时很合适）。
4. 是线程安全的。
5. 可以设置缓存 对象总数 和 “总开销”。

`NSCache`搭配`NSPureableData`使用：

```objectivec
typedef void(^EOCNetworkFetcherCompletionHander)(NSData *data);
NSCache *_cache;
- (instancetype)init {
  self = [super init];
  if (self) {
    _cache = [NSCache new];
    _cache.countLimit = 100; // 100 URLs
    _cache.totalCostLimit = 5 * 1024 * 1024; // 5MB
  }
  return self;
}
- (void)downloadDataForUrl:(NSURL *)url {
  NSPurgeableData *cachedData = [_cache objectForKey:url];
  if (cachedData) { // Cache hit
//    cachedData.isContentDiscarded // 相关内存是否已释放
    [cachedData beginContentAccess]; // 告诉它不应丢弃自己所占用的内存
    [self useData:cachedData];
    [cachedData endContentAccess]; // 告诉它在必要时可以丢弃自己所占用的内存
  } else { // Cache miss
    EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [fetcher startWithCompletionHander:^(NSData *data){
      NSPurgeableData *purgeableData = [NSPurgeableData dataWithData:data];
      [_cache setObject:purgeableData forKey:url cost:purgeableData.length];
      [self useData:purgeableData];
      [purgeableData endContentAccess];
    }];
  }
}
```

要点：

* 实现缓存时应选用`NSCache`而非`NSDictionary`对象。因为`NSCache`可以提供优雅的自动删减功能，而且是“线程安全的”，此外，它与字典不同，并不会拷贝键值
* 可以给`NSCache`对象设置上限，用以限制缓存中的对象总个数，但是绝不要把这些设置当成可靠的“硬限制”，他们仅对NSCache起指导作用
* 将`NSPurgeableData`与`NSCache`搭配使用，可实现自动清楚数据的功能。及当`NSPurgeableData`对象所占用内存被系统丢弃时，该对象自身也会从缓存中移除
* 如果缓存使用得当，那么应用程序的响应速度就能提高。只有那种“重新计算起来很费事的”数据，才值得放入缓存，如：需要从网络获取或从磁盘读取的数据
