0%

iOS知识点(1) - NSCache

最近在详细阅读React-Native源码,准备出一系列React-Native源码的解析及实现原理的连载文章,后续一一发布。 同时对遇到的iOS知识点进行梳理。基于React-Native 0.59.9版本的代码讲解。

本文讲的是NSCache,在RCTImage库中使用到的, 具体可以看 RCTImage/RCTImageCache.m中_decodedImageCache的使用。

RCTImage把获取到的图片缓存到RCTImageCache中,具体保存在_decodedImageCache。对于没有loaderHandler处理的url的图片, 会先从cache中查找,否则就从网络下载。

NSCache是一个用于临时存储键数据的可变集合,当内存紧张时可以自动释放对象,避免高内存。NSCache是线程安全的,可以从任何线程添加、删除、查询对象。
NSCache不会拷贝key,不同于NSdictionary。

包含可以释放的内容的对象可以实现NSDiscardableContent协议,来优化缓存管理。默认实现了该协议的对象,在内容被删除时会自动移除。移除之前,会调用discardContentIfPossible方法。

NSCache主要有几个属性 及 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@property NSUInteger totalCostLimit; // 最大缓存空间, 为0则不限制

@property NSUInteger countLimit; // 缓存对象的个数限制, 为0则不限制

@property BOOL evictsObjectsWithDiscardedContent; // 是否主动移除内容过期的对象,默认YES, 配合 NSDiscardableContent协议使用

- (nullable ObjectType)objectForKey:(KeyType)key; // 获取key的缓存
- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost 添加缓存对象, 占用空间0
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g; // 添加缓存对象, 占用空间g

- (void)removeObjectForKey:(KeyType)key; // 手动移除缓存

- (void)removeAllObjects; // 移除所有缓存对象

NSCache 通过设置totalCostLimit的值限制最大的缓存空间, 在添加缓存对象时会指定缓存对象的占用空间, 为0则不限制,默认为0。如果总的占用空间超过totalCostLimit。 NSCache会自动释放一些对象直到空间小于totalCostLimit。哪个对象会被释放是不确定的、释放时机也是不确定的,依赖NSCache的实现。

同样的 countLimit是对缓存的对象个数进行限制, 为0则不限制,默认为0. 当对象个数超过countLImit时,某些对象就会被释放掉。释放的时机和具体的对象也是不确定的。

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
28
29
30
-(void)downloadDataForURL:(NSURL *)url{
NSPurgeableData *cachedData = [_cache objectForKey:url]; // 实现NSDiscardableContent协议的对象
if(cachedData){
//Stop the data being purged
[cachedData beginContentAccess];

//Use the cached data
[self useData:cachedData];

//Mark that the data may be purged again
[cachedData endContentAccess];
}else{
//Cache miss
EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc]initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data) {
NSPurgeableData *purgeableData = [NSPurgeableData dataWithData:data];
[_cache setObject:purgeableData forKey:url cost:data.length];

//Don't need to beginContentAccess as it begins
//with access already marked

//Use the retrieved data
[self useData:data];

//Mark that the data may be purged now
[purgeableData endContentAccess];
}];
}
}

在RCTImage中设置缓存最大限制大小为20MB, 还根据NSURLResponse的缓存字段对缓存进行控制, 在获取缓存时,如果过期则主动删除缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
_decodedImageCache.totalCostLimit = 20 * 1024 * 1024; // 20 MB


- (UIImage *)imageForUrl:(NSString *)url
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
{
NSString *cacheKey = RCTCacheKeyForImage(url, size, scale, resizeMode);
@synchronized(_cacheStaleTimes) {
id staleTime = _cacheStaleTimes[cacheKey];
if (staleTime) {
if ([[NSDate new] compare:(NSDate *)staleTime] == NSOrderedDescending) {
// cached image has expired, clear it out to make room for others
[_cacheStaleTimes removeObjectForKey:cacheKey];
[_decodedImageCache removeObjectForKey:cacheKey];
return nil;
}
}
}
return [_decodedImageCache objectForKey:cacheKey];
}

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。