NSCache 的理解

1 NSCacheNSDictionary 的區(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)用了NSCopyingcopyWithZone方法之宿。對(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ā)生了變化比被。從 vretainCount屬性變化,能夠看出無(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ù)可以配合beginContentAccessendContentAccess進(jìn)行加/減俘枫,在其為 0 時(shí)腥沽,該對(duì)象可以使用discardContentIfPossible進(jìn)行內(nèi)存的及時(shí)回收。

4 內(nèi)存警告時(shí)的處理鸠蚪。

收到內(nèi)存警告時(shí)今阳,可以使用手動(dòng)移除NSCacheNSPurgeableData對(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í)NSCacheevictsObjectsWithDiscardedContent屬性為YES時(shí)(系統(tǒng)默認(rèn)為YES)妖谴,會(huì)自動(dòng)將NSPurgeableData移除。

5 NSCacheNSMutableDictinary作為緩存時(shí)的性能對(duì)比

這里貼一下某個(gè)前輩的兩個(gè)總結(jié):

性能1

性能2
總結(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

喜歡和關(guān)注都是對(duì)我的鼓勵(lì)和支持~
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末判沟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子崭篡,更是在濱河造成了極大的恐慌,老刑警劉巖吧秕,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琉闪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡砸彬,警方通過(guò)查閱死者的電腦和手機(jī)颠毙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)砂碉,“玉大人蛀蜜,你說(shuō)我怎么就攤上這事≡霾洌” “怎么了滴某?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)滋迈。 經(jīng)常有香客問(wèn)我霎奢,道長(zhǎng),這世上最難降的妖魔是什么饼灿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任幕侠,我火速辦了婚禮,結(jié)果婚禮上碍彭,老公的妹妹穿的比我還像新娘晤硕。我一直安慰自己,他們只是感情好庇忌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布舞箍。 她就那樣靜靜地躺著,像睡著了一般皆疹。 火紅的嫁衣襯著肌膚如雪创译。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天墙基,我揣著相機(jī)與錄音软族,去河邊找鬼。 笑死残制,一個(gè)胖子當(dāng)著我的面吹牛立砸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播初茶,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼颗祝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起螺戳,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搁宾,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后倔幼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盖腿,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年损同,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翩腐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膏燃,死狀恐怖茂卦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情组哩,我是刑警寧澤等龙,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站伶贰,受9級(jí)特大地震影響而咆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幕袱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一暴备、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧们豌,春花似錦涯捻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辩尊,卻和暖如春涛浙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摄欲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工轿亮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胸墙。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓我注,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親迟隅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子但骨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345