1 NSCache
和 NSDictionary
的區(qū)別
首先看一下 Apple
對(duì) NSCache
的介紹:
* 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.
簡(jiǎn)單翻譯一下:
(1) NSCache
內(nèi)部含有自動(dòng)丟棄策略,保證不占用過(guò)多內(nèi)存摔寨;
(2) 線(xiàn)程安全杆勇;
(3) 和 NSMutableDictionary
不同航厚,存儲(chǔ)時(shí)不會(huì)對(duì) key
進(jìn)行copy
尝艘。
對(duì)NSCache
的 3 個(gè)特點(diǎn)可以這樣理解:內(nèi)部的機(jī)制赴穗,保證了存入的緩存數(shù)據(jù)能夠及時(shí)釋放椎侠;內(nèi)部含鎖第租,保證線(xiàn)程安全,相比 NSDictionary
, 執(zhí)行效率會(huì)降低肺蔚;對(duì) key
不拷貝煌妈,能帶來(lái)執(zhí)行效率的提升。
對(duì)特點(diǎn) 3 舉例驗(yàn)證:
UIView *v = [UIView new];
NSMutableArray *array = [NSMutableArray array];
[self.cache setObject:array forKey:v];
[self.dict setObject:array forKey:v];
NSMutableArray
類(lèi)實(shí)現(xiàn)了NSCopying
協(xié)議宣羊, 而UIView
類(lèi)沒(méi)有璧诵。上述代碼在執(zhí)行到最后一行時(shí),會(huì)報(bào)錯(cuò):
reason: '-[UIView copyWithZone:]: unrecognized selector sent to instance 0x7fe29fe097c0'
說(shuō)明 NSDictionary
在存儲(chǔ)時(shí)仇冯,調(diào)用了NSCopying
的copyWithZone
方法之宿。對(duì)代碼進(jìn)行如下調(diào)整:
UIView *v = [UIView new];
NSMutableArray *array = [NSMutableArray array];
[self.cache setObject:v forKey:array];
[self.dict setObject:v forKey:array];
執(zhí)行成功。在控制臺(tái)查詢(xún)更詳細(xì)信息:
(lldb) po [v valueForKey:@"retainCount"]
2
(lldb) po [v valueForKey:@"retainCount"]
3
(lldb) po array
<__NSArrayM 0x600002d223a0>(
)
(lldb) po [self.dict allKeys]
<__NSSingleObjectArrayI 0x600002170370>(
<__NSArray0 0x7fff8062d430>(
)
能夠看到NSArray
作為 NSDictionary
使用后苛坚,其內(nèi)存地址發(fā)生了變化比被。從 v
的retainCount
屬性變化,能夠看出無(wú)論是NSCache
, 還是NSDictionary
泼舱,存儲(chǔ)時(shí)等缀,都是將value
值的retainCount
增加,也就是對(duì)值的淺copy
娇昙。
2 NSCache
的使用
使用NSCache
作為容器使用尺迂,在其delegate
方法- (void)cache:(NSCache *)cache willEvictObject:(id)obj
中,能夠看到其移除內(nèi)部數(shù)據(jù)的過(guò)程。
將NSCache
設(shè)置足夠的個(gè)數(shù)和容量噪裕,反復(fù) 3 次讀取圖片數(shù)據(jù)在NSCache
中存儲(chǔ)蹲盘,然后將 App
退到后臺(tái)。
- (NSCache *)cache {
if (!_cache) {
_cache = [NSCache new];
_cache.countLimit = 10;
//byte計(jì)算
_cache.totalCostLimit = 100000;
_cache.delegate = self;
_cache.evictsObjectsWithDiscardedContent = YES;
}
return _cache;
}
#pragma mark - NSCacheDelegate
- (void)cache:(NSCache *)cache willEvictObject:(id)obj {
NSLog(@"刪除數(shù)據(jù):%@", obj);
}
- (void)btnClick {
for(int i = 0;i < 3;i++){
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:@"icon_big.png" ofType:nil]];
[self.cache setObject:data forKey:[NSString stringWithFormat:@"data_%d",i] cost:0];
NSLog(@"存入數(shù)據(jù)%d:%@", i, data);
}
}
觀(guān)察控制臺(tái)輸出數(shù)據(jù):
2020-06-16 10:22:29.213051+0800 NSCacheTest[70324:1233069] 存入數(shù)據(jù)0:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:22:29.213375+0800 NSCacheTest[70324:1233069] 存入數(shù)據(jù)1:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:22:29.213715+0800 NSCacheTest[70324:1233069] 存入數(shù)據(jù)2:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:22:33.587077+0800 NSCacheTest[70324:1233069] 刪除數(shù)據(jù):{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:22:33.587236+0800 NSCacheTest[70324:1233069] 刪除數(shù)據(jù):{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:22:33.587356+0800 NSCacheTest[70324:1233069] 刪除數(shù)據(jù):{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
能夠看出App進(jìn)入后臺(tái)時(shí)膳音,NSCache主動(dòng)釋放內(nèi)部全部數(shù)據(jù)
召衔。
3 NSPurgeableData
查找網(wǎng)上NSCache
資料時(shí),發(fā)現(xiàn)很多均連帶了NSPurgeableData
祭陷。查詢(xún)NSPurgeableData
的文檔介紹中苍凛,有這么一段:
You may use these objects by themselves, and do not necessarily have
to use them in conjunction with NSCache to get the purging behavior.
The NSCache class incorporates a caching mechanism with some auto-removal
policies to ensure that its memory footprint does not get too large.
大意是說(shuō)NSPurgeableData
可以獨(dú)立NSCache
使用,NSCache內(nèi)部有一些自動(dòng)清理機(jī)制兵志,保證不占用太大內(nèi)存毫深。對(duì)這么多
NSCache文章中連帶介紹
NSPurgeableData`原因,感到有些疑惑毒姨。
將上述存入NSCache
中的數(shù)據(jù)哑蔫,替換成NSPurgeableData
, 然后讓app
進(jìn)入后臺(tái)。
for(int i = 0;i < 3;i++){
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon_big.png" ofType:nil]];
NSPurgeableData *data2 = [NSPurgeableData dataWithData: data];
[self.cache setObject:data2 forKey:[NSString stringWithFormat:@"image_%d",i] cost:data2.length];
[data2 endContentAccess];
NSLog(@"存入數(shù)據(jù)%d:%@", i, data);
}
2020-06-16 10:27:00.632593+0800 NSCacheTest[70567:1239254] 存入數(shù)據(jù)0:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:27:00.632939+0800 NSCacheTest[70567:1239254] 存入數(shù)據(jù)1:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
2020-06-16 10:27:00.633179+0800 NSCacheTest[70567:1239254] 存入數(shù)據(jù)2:{length = 19050, bytes = 0x89504e47 0d0a1a0a 00000004 43674249 ... 49454e44 ae426082 }
從控制臺(tái)的結(jié)果中弧呐,發(fā)現(xiàn)NSCache
并沒(méi)有自動(dòng)清除NSPurgeableData
的過(guò)程闸迷。個(gè)人理解NSPurgeableData
的自動(dòng)清除是不正確的,而是其數(shù)據(jù)內(nèi)容的計(jì)數(shù)可以配合beginContentAccess
和endContentAccess
進(jìn)行加/減俘枫,在其為 0 時(shí)腥沽,該對(duì)象可以使用discardContentIfPossible
進(jìn)行內(nèi)存的及時(shí)回收。
4 內(nèi)存警告時(shí)的處理鸠蚪。
收到內(nèi)存警告時(shí)今阳,可以使用手動(dòng)移除NSCache
和NSPurgeableData
對(duì)象,及時(shí)釋放占用的內(nèi)存茅信。
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
[self.cache removeAllObjects];
//內(nèi)存清除后盾舌,在 cache 屬性 evictsObjectsWithDiscardedContent 為 YES(默認(rèn))時(shí),會(huì)自動(dòng)從NSCache中移除
[self.data discardContentIfPossible];
}
其中NSCache
中存儲(chǔ)的NSPurgeableData
中內(nèi)容被discard
成功時(shí)蘸鲸,同時(shí)NSCache
的evictsObjectsWithDiscardedContent
屬性為YES
時(shí)(系統(tǒng)默認(rèn)為YES)妖谴,會(huì)自動(dòng)將NSPurgeableData
移除。
5 NSCache
和NSMutableDictinary
作為緩存時(shí)的性能對(duì)比
這里貼一下某個(gè)前輩的兩個(gè)總結(jié):
總結(jié)
相比NSDictionary
酌摇,使用 NSCache
作為緩存能夠?qū)⒄加玫膬?nèi)存及時(shí)回收膝舅,進(jìn)入后臺(tái)時(shí)會(huì)主動(dòng)將內(nèi)部存儲(chǔ)的數(shù)據(jù)進(jìn)行釋放,而且在滿(mǎn)足條件時(shí)窑多,會(huì)自動(dòng)移除NSPurgeableData
類(lèi)型的數(shù)據(jù)仍稀。NSCache
并非完美的緩存,因?yàn)榫€(xiàn)程安全的存在埂息,效率欠佳技潘。
參考:
1 https://juejin.im/entry/5948bd53fe88c2006a93744e
2 https://juejin.im/post/5d85dfe7e51d4561f777e28d
3 http://blog.ipalfish.com/?author=22