通常一個APP分為網(wǎng)絡践美、數(shù)據(jù)谋作、業(yè)務等多個層級旺坠。
只看數(shù)據(jù)層、最基本的模式如下馋缅。
VC從DB層讀取數(shù)據(jù)、交由View進行展示绢淀。
然而萤悴、當同一個VC需要多次讀取、或者多個VC都需要像DB讀取數(shù)據(jù)的時候皆的。就會出現(xiàn)如下問題覆履。
-
同一個資源被多次IO讀取、就會出現(xiàn)資源的浪費。
于是硝全、通常我們都會在DB與VC之間栖雾、插入一層緩存。
將DB讀取出來的數(shù)據(jù)伟众、交由Cache單例持有析藕。
- 同一個DB資源不再需要被多次讀取、VC只需要從單例Cache中獲取數(shù)據(jù)即可赂鲤。
但使用單例對象噪径、又會出現(xiàn)另外一個問題。
線程安全数初。
當在有一個線程操作一個單例對象找爱、另一個線程同時使用了這個對象、哪怕只是讀取泡孩、一樣會出現(xiàn)崩潰车摄。
針對多線程的資源搶奪、幾個解決方案仑鸥。
-
原子鎖
@property (atomic) NSArray * walletDatas;
這樣吮播、將cache中會被改變的對象都加上atomic就行了。
但是眼俊、過多的atomic會拖慢app性能意狠。過多的使用鎖也可能導致一些難以捕捉的bug。
修正一下疮胖、即使是atomic环戈、也只能保證set&&get方法的線程安全。在進行對象操作的時候澎灸。也是要崩潰的院塞。
詳見我的另一篇博客:傳送門
所以、需要在每個對象的對象方法操作中性昭、也加入線程鎖拦止。
- 分線程cache
將每一個線程單獨分配一個cache。每個線程中的讀寫糜颠、只操作對應線程中的cache汹族、也可以解決資源搶奪的問題。
但是需要明確的知曉哪次讀寫其兴、在哪個線程中進行顶瞒。線程間數(shù)據(jù)的同步也很麻煩。
-
readonly
@property (nonatomic,readonly) NSArray * walletDatas;
這是我們采用的方案忌警。
緩存中的屬性搁拙、外部只允許讀取秒梳、不允許寫入。
數(shù)據(jù)如何更改呢箕速?
當需要修改數(shù)據(jù)源酪碘、需要調用cache對應的方法。
//從數(shù)據(jù)庫更新數(shù)據(jù)
- (void)updata;
//插入
- (void)pushWalletData:(BSWalletData *)data callback:(walletCacheCallBack)callback;
//刪除
- (void)delWalletDataWithPrivateKey:(NSString *)privateKey callback:(walletCacheCallBack)callback;;
//刪除全部
- (void)delAllWalletData:(walletCacheCallBack)callback;
- (void)setDefaultWalletData:(BSWalletData *)walletData callback:(walletCacheCallBack)callback;
內(nèi)部的實現(xiàn)大致為一些邏輯判斷盐茎、資源格式化兴垦、然后嘗試對DB進行操作。
如果操作成功字柠、再對cache單例中的緩存數(shù)據(jù)進行更新探越。
更新、便是將cache中的整個屬性進行替換窑业。
但是這樣處理钦幔、又會出現(xiàn)兩個問題。
-
如何保證替換的時候不會正好因為其他線程也在數(shù)據(jù)讀取常柄、導致crash鲤氢。
-
在替換之后、上層業(yè)務者如何依舊持有者舊的數(shù)據(jù)西潘、怎樣更新卷玉。
針對以上兩個問題、我們的解決方案大致如下:
@interface BSWalletCache : NSObject
@property (nonatomic ,readonly) BSWalletDataSource * walletDataSrource;
@end
@interface BSWalletCache()
@property (atomic) BSWalletDataSource * dataSource;
@end
@implementation BSWalletCache
- (BSWalletDataSource *)walletDataSrource {
return self.dataSource;
}
@end
***************
@interface BSWalletDataSource : NSObject
@property (nonatomic) BOOL dirty;
@property (nonatomic,readonly) NSArray * walletDatas;
@end
原理大致如下:
針對公司的項目喷市、我在Cache層與DB層之間又加了一個中間層Mediater相种。
一是為了將Cache更好的抽離出來做去處理緩存。
二是更好的應對將來的需求變化(存儲方式品姓、格式化標準等)寝并。
NSCache
解釋下為什么不用NSCache
NSCache在存取的時候是線程安全的沒錯。但是和屬性的atomic一樣缭黔、進行對象操作的時候并不安全食茎。
Demo
demo是直接把剛寫好的公司模塊蒂破、脫敏之后拿出來的馏谨。沒什么UI、只是寫了幾個button測試附迷。
《有興趣可以自取》
最后
本文主要是自己的學習與總結惧互。如果文內(nèi)存在紕漏、萬望留言斧正喇伯。如果不吝賜教小弟更加感謝喊儡。