iOS -- 構(gòu)建緩存時選用 NSCache (32)

構(gòu)建緩存時選用 NSCache

?開發(fā) Mac OS X 或 iOS 應用程序時,經(jīng)常會遇到一個問題,那就是從網(wǎng)上下載的圖片應如何來緩存,NSCache 類就是 Foundation 框架專為處理這種任務而設計的.

?NSCache 的優(yōu)點在于,當系統(tǒng)資源將要耗盡時,它可以自動刪減緩存,此外,NSCache 還會先行刪減'最久未使用的對象'.?

另外,NSCache 是線程安全的,意思就是:在開發(fā)者自己不編寫加鎖代碼的前提下,多個線程便可以同時訪問 NSCache, 對緩存來說,線程安全非常重要,因為開發(fā)者可能要在某個線程中讀取數(shù)據(jù),此時如果發(fā)現(xiàn)緩存里面找不到指定的鍵,那么就下載該鍵所對應的數(shù)據(jù)了,而下載完數(shù)據(jù)之后所要執(zhí)行的回調(diào)函數(shù),有可能會放在背景線程中運行, 這樣的話,就等于是用另外一個線程來寫入緩存 了.?

開發(fā)者可以操控緩存刪減其內(nèi)容的時機.有兩個與系統(tǒng)資源相關(guān)的尺度可供調(diào)整, ?其一是緩存的對象總數(shù), ?其二是所有對象的 '總開銷', 開發(fā)者在將對象加入緩存時, 可為其指定 '開銷值', 當對象總數(shù) 或 總開銷 超過上限時,緩存就可能會刪減其中的對象了, 在可用的系統(tǒng)資源趨于緊張時, 也會這么做,然而要注意, '可能'會刪減某個對象, 并不意味著 '一定'會刪減這個對象, 刪減對象時所遵循的順序, 由具體實現(xiàn)來定,這尤其說明: 想通過調(diào)整 '開銷值'來迫使緩存優(yōu)先刪減某對象, 不是個好辦法.?

向緩存中添加對象時,只有在很快計算出 '開銷值'的情況下,才應該采用'總開銷'這個尺度,若是計算過程很復雜,那么照這種方式來使用緩存就達不到最佳效果了.因為每次向緩存 中放入對象,還要專門花時間來計算這個附加因素的值, 而緩存的本意則是要增加應用程序響應用戶操作的速度.比方說,如果計算'開銷值'時必須訪問磁盤才能確定文件大小, 或是必須訪問數(shù)據(jù)庫才能決定具體數(shù)值, 那就不好了, 但是,如果要加入緩存中的是 NSData 對象, 那么就不妨指定 '開銷值',可以把數(shù)據(jù)大小當做 '開銷值'來用,因為 NSData 對象的數(shù)據(jù)大小是 已知的,所及計算'開銷值'的過程只不過是讀取一項屬性. 例如:??

#import// 網(wǎng)絡訪問類

typedef void(^NetworkFetcherCompletionHandler)(NSData * data);

@interface NetworkFetcher : NSObject

- (id)initWithURL:(NSURL *)url;

- (void)startWithCompletionHandler:(NetworkFetcherCompletionHandler)handler;

@end

// 用戶網(wǎng)絡獲取和緩存結(jié)果的類

@interface NSCacheClass : NSObject

@end

@implementation NSCacheClass{

? ? ? ?NSCache * _cache;

}

- (id)init{

? ? ? ? if (self = [super init]){

? ? ? ? _cache = [NSCache new];

? ? ? ? // 緩存的最大值是 100 條 URL

? ? ? ?_cache.countLimit = 100;

? ? ? // 5MB 的 總開銷 限制. ??

? ? ? _cache.totalCostLimit = 5 * 1024 * 1024;

? ? ?}

? ? ? ?return self;

}

- (void)downloadDataForURL:(NSURL *)url{

? ? ? ? ? NSData * cachedData = [_cache objectForKey:url];

? ? ? ? ? if(cachedData){

? ? ? ? ? ? ? ? [self useData:cachedData];

? ? ? ? ? }else{

? ? ? ? ? ? ? ?NetworkFetcher * fetcher = [[NetworkFetcher alloc]initWithURL:url];

? ? ? ? ? ? ? ?[fetcher startWithCompletionHandler:^(NSData * data){

? ? ? ? ? ? ? ? ? ? ? ? ? [_cache setobject:data forKey:url cost:data.length];

? ? ? ? ? ? ? ? ? ? ? ? ? [self useData:data];

? ? ? ? ? ? ? ? }];

? ? ? ? ? ?}

}

@end

在本例中, 下載數(shù)據(jù)所用的 URL ,就是緩存的鍵, 若緩存不存在, 則下載數(shù)據(jù)并將其放入緩存, 而數(shù)據(jù)的'開銷值'則為其長度, 創(chuàng)建 NSCache 時,將其中可緩存的總對象數(shù)目上限設為 100,將'總開銷'上限設為 5MB ,由于'開銷量'以字節(jié)為單位, 所以要通過算式將 MB 換算成 字節(jié).

還有個類叫做 NSPurgeableData (可清除的 data), 和 NSCache 搭配起來用, 效果很好,此類是 NSMutableData 的子類,而且實現(xiàn) NSDiscardableContent 協(xié)議(可丟棄的內(nèi)容協(xié)議), 如果某個對象所占的內(nèi)存能夠根據(jù)需要隨時丟棄, 那么就可以實現(xiàn)該協(xié)議所定義的接口, 這就是說, 當系統(tǒng)資源緊張時, 可以把保存 NSPurgeableData 對象的那塊內(nèi)存釋放掉, NSDiscardableContent 協(xié)議里面定義了名為 isContentDiscarded 的方法, 可以用來查詢相關(guān)內(nèi)存是否已經(jīng)釋放.

如果需要訪問某個 NSPuargeableData 對象, 可以調(diào)用其 beginContentAccess 方法, 告訴它現(xiàn)在還不應丟棄自己所占據(jù)的內(nèi)存, 用完之后, 調(diào)用 endContentAccess 方法, 告訴它在必要的時候可以丟棄自己所占據(jù)的那塊內(nèi)存, 這些調(diào)用可以嵌套, 所以說,衙門就像遞增遞減引用計數(shù)一樣,只有對象的 '引用計數(shù)'為 0 的時候才可以丟棄.

如果將 NSPurgeableData 對象加入 NSCathe ,那么當該對象為系統(tǒng)所丟棄時, 也會自動從緩存中移除, 通過 NSCache 的 evictsObjectsWithDiscardedContent (移除廢棄的內(nèi)容) 屬性, 可以開啟或關(guān)閉此 功能.

剛才的例子可以改寫為:

- (void)downloadDataForURL:(NSURL *)url{

? ? ? ? ? ? NSPurgeableData * cachedData = [_cache objectForKey:url];

? ? ? ? ? ? ?if(cachedData){

? ? ? ? ? ? ? ? ? ? [cacheData beginContentAccess];

? ? ? ? ? ? ? ? ? ? [self useData:cachedData];

? ? ? ? ? ? ? ? ? ? [cacheData endContentAccess];

? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ?NetworkFetcher * fetcher = [[NetworkFetcher alloc]initWithURL:url];

? ? ? ? ? ? ? ? ? [fetcher startWithCompletionHandler:^(NSData * data){

? ? ? ? ? ? ? ? ? ? ? ? ? ? NSPurgeableData * purgeableData = [NSPurgeableData dataWithData:data];

? ? ? ? ? ? ? ? ? ? ? ? ? ?[_cache setobject:data forKey:url cost:purgeableData.length];

? ? ? ? ? ? ? ? ? ? ? ? ? ?[self useData:data];

? ? ? ? ? ? ? ? ? ? ? ? ? ?[purgeableData endContentAccess];

? ? ? ? ? ? ? ? ? }];

? ? ? ? ? ?}

}

注意: 創(chuàng)建好了 NSPurgeableData 對象之后, 其 'purge 引用計數(shù)' 會多一, 所以無須再調(diào)用 beginContentAccess 了, 然而其后必須調(diào)用 endContentAccess ,將多出來的 "1" 抵消.

總結(jié):

NSCathe 提供了自動刪減功能, 而且是'線程安全的'.

可以給 NSCache 對象設置上限, 用以限制緩存中的對象總個數(shù) 及 '總成本',而這些尺度則定義了 緩存刪減其中對象的時機. 但是絕對不要把這些尺度當成可靠的 '硬限制',它們僅對 NSCache 其指導作用.

將 NSPurgeableData 與 NSCache 搭配使用, 可實現(xiàn)自動清除數(shù)據(jù)的功能, 也就是說, 當 NSPurgeableData 對象所占據(jù)的內(nèi)存為系統(tǒng)所丟棄時, 該對象自身也會從緩存中移除.

如果緩存使用得當, 那么應用程序的響應速度就能提高, 只有那種 '重新計算起來很麻煩的'數(shù)據(jù),才值得放入緩存, 比如那些需要從網(wǎng)絡獲取或 從 磁盤讀取的數(shù)據(jù).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 第7章 系統(tǒng)框架 第47條:熟悉系統(tǒng)框架 框架:將一系列代碼封裝為動態(tài)庫(dynamic library),并在其...
    獨木舟的木閱讀 420評論 0 0
  • 1 第一節(jié) 1.多有塊枚舉,少用for循環(huán) 遍歷collection有四種方法,最基本的辦法是for循環(huán),其次是N...
    誰動了MyWorld閱讀 533評論 3 7
  • 微信圈,一友發(fā)了一組多肉,許久不聯(lián)系,很好奇她什么時候多了一項愛好,便聊了起來?!安煌娑嗳?,還能干嘛?”,...
    life_240b閱讀 692評論 0 5
  • 生活總是有很多道選擇題,總是在考慮如何才能做出最為正確的選擇。而人的本性是自私的,且是趨利避害的,但是這種本性往往...
    呢喃91閱讀 336評論 0 0

友情鏈接更多精彩內(nèi)容