轉(zhuǎn)載請(qǐng)聯(lián)系作者獲取授權(quán),并標(biāo)明文章作者蓬痒,謝謝!
iOS開(kāi)發(fā)中經(jīng)常會(huì)遇到數(shù)據(jù)本地化或者是圖片緩存到本地,本文將會(huì)解讀一個(gè)比較優(yōu)秀的數(shù)據(jù)緩存開(kāi)源庫(kù)——YYCache(作者:ibireme)惧盹,從源碼上分析YYCache中優(yōu)秀的緩存數(shù)據(jù)結(jié)構(gòu)和LRU緩存淘汰算法乳幸。
目錄
YYCache的源碼結(jié)構(gòu)
?YYCache
?YYDiskCache
?YYKVStorage
?YYMemoryCache
YYCache的存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)(核心)
?Memory
??鏈表節(jié)點(diǎn)模型
??存儲(chǔ)流程分析
?Disk
??數(shù)據(jù)庫(kù)模型對(duì)象
??存儲(chǔ)流程分析
LRU緩存淘汰算法實(shí)現(xiàn)
?Memory-LRU
?Disk-LRU
總結(jié)
YYCache的源碼結(jié)構(gòu)
YYCache
代碼對(duì)外的接口,提供大部分的對(duì)外接口钧椰,使用者拿該類中的接口就可以使用粹断,內(nèi)部實(shí)現(xiàn)基本上是調(diào)用后面幾個(gè)類中的接口。
初始化方法:
- (instancetype)initWithName:(NSString *)name;
- (instancetype)initWithPath:(NSString *)path;
object set and get:
//是否包含以這個(gè)key存儲(chǔ)的對(duì)象嫡霞,同步接口阻塞線程
- (BOOL)containsObjectForKey:(NSString *)key;
//是否包含以這個(gè)key存儲(chǔ)的對(duì)象瓶埋,異步回調(diào)接口不阻塞線程
- (void)containsObjectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, BOOL contains))block;
//下面這些方法見(jiàn)名知意
- (nullable id<NSCoding>)objectForKey:(NSString *)key;
- (void)objectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, id<NSCoding> object))block;
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key;
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key withBlock:(nullable void(^)(void))block;
- (void)removeObjectForKey:(NSString *)key;
*key))block;
//帶有刪除進(jìn)度的刪除接口
- (void)removeAllObjectsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
endBlock:(nullable void(^)(BOOL error))end;
通過(guò)name初始化的對(duì)象會(huì)默認(rèn)將緩存路徑設(shè)置為Document目錄下Cache路徑下名字為name的文件夾。第二個(gè)方法見(jiàn)名知意直接以path作為緩存路徑诊沪。
YYDiskCache
源碼中負(fù)責(zé)磁盤緩存的類养筒,細(xì)化為磁盤文件緩存和磁盤數(shù)據(jù)庫(kù)緩存。里面大多也是提供相應(yīng)接口端姚,方法實(shí)現(xiàn)大多是調(diào)用YYKVStorage中的接口晕粪。
初始化方法:
- (nullable instancetype)initWithPath:(NSString *)path;
屬性:
//磁盤存儲(chǔ)方式閥值。根據(jù)作者(ibireme)的測(cè)試渐裸,當(dāng)存儲(chǔ)的數(shù)據(jù)大于某個(gè)閥值時(shí)直接存儲(chǔ)到文件會(huì)比存儲(chǔ)到數(shù)據(jù)庫(kù)效率高巫湘,該屬性默認(rèn)閥值是20480(20kb),手動(dòng)設(shè)置后則會(huì)根據(jù)這個(gè)閥值決定數(shù)據(jù)存儲(chǔ)位置
@property (readonly) NSUInteger inlineThreshold;
//自定義數(shù)據(jù)編解碼方式昏鹃,當(dāng)對(duì)數(shù)據(jù)進(jìn)行編解碼時(shí)會(huì)先調(diào)用該block如果不為空則使用自定義的編解碼方式尚氛,默認(rèn)使用NSKeyedArchiver編解碼
@property (nullable, copy) NSData *(^customArchiveBlock)(id object);
@property (nullable, copy) id (^customUnarchiveBlock)(NSData *data);
//自定義key的存儲(chǔ)名字,不設(shè)置默認(rèn)為將key進(jìn)行一次md5加密
@property (nullable, copy) NSString *(^customFileNameBlock)(NSString *key);
//下面LRU淘汰條件洞渤,最大存儲(chǔ)空間阅嘶,最大存儲(chǔ)數(shù)量,最長(zhǎng)存儲(chǔ)時(shí)間载迄,磁盤最小剩余空間
@property NSUInteger costLimit;
@property NSUInteger countLimit;
@property NSTimeInterval ageLimit;
//定時(shí)檢查這些限制的定時(shí)器執(zhí)行時(shí)間
@property NSTimeInterval autoTrimInterval;
@property NSUInteger freeDiskSpaceLimit;
后面的數(shù)據(jù)get和set方法用法基本和YYCache類中一致讯柔,額外添加了數(shù)據(jù)附加數(shù)據(jù):
+ (void)setExtendedData:(nullable NSData *)extendedData toObject:(id)object;
+ (nullable NSData *)getExtendedDataFromObject:(id)object;
YYKVStorage
YYDiskCache類中的接口實(shí)現(xiàn)抡蛙,內(nèi)部實(shí)現(xiàn)了磁盤文件存儲(chǔ)和磁盤數(shù)據(jù)庫(kù)存儲(chǔ)。
里面的內(nèi)部類:
//數(shù)據(jù)庫(kù)模型對(duì)象
@interface YYKVStorageItem : NSObject
//數(shù)據(jù)存儲(chǔ)方式
//文件:YYKVStorageTypeFile
//數(shù)據(jù)庫(kù):YYKVStorageTypeSQLite
//自適應(yīng):YYKVStorageTypeMixed
@property (nonatomic, readonly) YYKVStorageType type;
下面就是一些根據(jù)key的get和set方法磷杏。這里不做過(guò)多解釋溜畅,后面會(huì)直接分析內(nèi)部實(shí)現(xiàn)。
YYMemoryCache
YYCache的內(nèi)存存儲(chǔ)對(duì)象极祸,主要實(shí)現(xiàn)了內(nèi)存存儲(chǔ)數(shù)據(jù)慈格。里面的LRU淘汰條件和YYDiskCache一致,和YYDiskCache區(qū)別在于多了一個(gè)最小剩余空間的限制遥金。由于是內(nèi)存緩存所以需要監(jiān)聽(tīng)內(nèi)存是否過(guò)大導(dǎo)致占用過(guò)多CPU資源浴捆。object的set和get方法和YYDiskCache中使用方法一致。
//當(dāng)內(nèi)存警告時(shí)是否刪除所有內(nèi)存中的緩存數(shù)據(jù)
@property BOOL shouldRemoveAllObjectsOnMemoryWarning;
//當(dāng)App進(jìn)入后臺(tái)時(shí)是否刪除所有內(nèi)存中的緩存數(shù)據(jù)
@property BOOL shouldRemoveAllObjectsWhenEnteringBackground;
//內(nèi)存警告時(shí)的回調(diào)
@property (nullable, copy) void(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache);
//進(jìn)入后臺(tái)時(shí)的回調(diào)
@property (nullable, copy) void(^didEnterBackgroundBlock)(YYMemoryCache *cache);
//是否在主線程釋放對(duì)象
@property BOOL releaseOnMainThread;
YYCache的存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)(核心)
下面我們按照Memory - File - Storage的順序來(lái)分析源碼中的數(shù)據(jù)結(jié)構(gòu)和實(shí)現(xiàn)代碼稿械。LRU算法將在后面解讀选泻。
Memory
內(nèi)存存儲(chǔ)中主要以雙向鏈表+CFDictionary的方式來(lái)存儲(chǔ),利用鏈表的有序性來(lái)存儲(chǔ)數(shù)據(jù)的存儲(chǔ)順序美莫,LRU算法也是通過(guò)限制條件從鏈表的尾部開(kāi)始刪除页眯。利用字典的key-value讀取數(shù)據(jù)快的特性用來(lái)快速讀寫(xiě)數(shù)據(jù)。
鏈表節(jié)點(diǎn)模型:
@interface _YYLinkedMapNode : NSObject {
@package
//前置指針
__unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
//后置指針
__unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
//存儲(chǔ)key
id _key;
//存儲(chǔ)數(shù)據(jù)
id _value;
//數(shù)據(jù)大小
NSUInteger _cost;
//數(shù)據(jù)最后一次讀取時(shí)間
NSTimeInterval _time;
}
下面是雙向鏈表類厢呵,里面實(shí)現(xiàn)了數(shù)據(jù)的get和set
@interface _YYLinkedMap : NSObject {
@package
CFMutableDictionaryRef _dic; // do not set object directly
NSUInteger _totalCost;
NSUInteger _totalCount;
_YYLinkedMapNode *_head; // MRU, do not change it directly
_YYLinkedMapNode *_tail; // LRU, do not change it directly
BOOL _releaseOnMainThread;
BOOL _releaseAsynchronously;
}
//下面的方法見(jiàn)名知意窝撵,這里不做過(guò)多翻譯
/// Insert a node at head and update the total cost.
/// Node and node.key should not be nil.
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
/// Bring a inner node to header.
/// Node should already inside the dic.
- (void)bringNodeToHead:(_YYLinkedMapNode *)node;
/// Remove a inner node and update the total cost.
/// Node should already inside the dic.
- (void)removeNode:(_YYLinkedMapNode *)node;
/// Remove tail node if exist.
- (_YYLinkedMapNode *)removeTailNode;
/// Remove all node in background queue.
- (void)removeAll;
YYMemoryCache,初始化一個(gè)雙向鏈表,一個(gè)線程安全鎖和一個(gè)異步隊(duì)列
pthread_mutex_t _lock;
_YYLinkedMap *_lru;
dispatch_queue_t _queue;
存儲(chǔ)流程分析
首先調(diào)用的對(duì)外接口YYCache -
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key
此時(shí)YYChahe類會(huì)現(xiàn)在內(nèi)存中查找如果有則返回襟铭,如果沒(méi)有再去磁盤中找碌奉,如果找到了則返回并將數(shù)據(jù)存儲(chǔ)到內(nèi)存中,畢竟內(nèi)存的存取速度要快幾倍于磁盤的存取速度寒砖。代碼如下:
- (id<NSCoding>)objectForKey:(NSString *)key {
id<NSCoding> object = [_memoryCache objectForKey:key];
if (!object) {
object = [_diskCache objectForKey:key];
if (object) {
[_memoryCache setObject:object forKey:key];
}
}
return object;
}
內(nèi)存中存數(shù)據(jù)的方法:YYMemoryCache -
- (void)setObject:(id)object forKey:(id)key
該方法中首先在字典中g(shù)et一下赐劣,如果有則將數(shù)據(jù)替換,并將數(shù)據(jù)的_time屬性換成當(dāng)前時(shí)間哩都,對(duì)應(yīng)的鏈表節(jié)點(diǎn)也將移到鏈表頭部魁兼,代表該數(shù)據(jù)是最近被讀取過(guò)的。
如果沒(méi)有則創(chuàng)建新的鏈表節(jié)點(diǎn)存儲(chǔ)到頭部漠嵌。
下面是將鏈表節(jié)點(diǎn)移動(dòng)到頭部的方法璃赡,添加新節(jié)點(diǎn)到頭部的方法:
[_lru bringNodeToHead: node]
[_lru insertNodeAtHead:node]
每次存完數(shù)據(jù)會(huì)通過(guò)LRU淘汰條件判斷一下是否需要淘汰數(shù)據(jù):
if (_lru->_totalCost > _costLimit)
if (_lru->_totalCount > _countLimit)
之后會(huì)根據(jù)_releaseAsynchronously
屬性和_releaseOnMainThread
屬性來(lái)決定淘汰掉的節(jié)點(diǎn)數(shù)據(jù)在哪個(gè)線程釋放:
if (_lru->_releaseAsynchronously) {
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
[node class]; //hold and release in queue
});
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
dispatch_async(dispatch_get_main_queue(), ^{
[node class]; //hold and release in queue
});
}
內(nèi)存中取數(shù)據(jù)的方法:
YYMemoryCache -
- (id)objectForKey:(id)key
該方法會(huì)從數(shù)據(jù)字典中找到對(duì)應(yīng)的value,并將鏈表中該節(jié)點(diǎn)的時(shí)間換成最新且移動(dòng)到鏈表頭部献雅。
Disk
磁盤緩存分為兩部分:文件緩存和數(shù)據(jù)庫(kù)緩存。根據(jù)前面提到的閥值屬性:inlineThreshold
來(lái)判定數(shù)據(jù)應(yīng)該存儲(chǔ)到哪里塌计。
首先YYDiskCache變量創(chuàng)建于YYCache的構(gòu)造方法里挺身,如果YYCache變量被釋放則會(huì)導(dǎo)致YYDiskCache變量也被釋放則無(wú)法執(zhí)行后續(xù)的數(shù)據(jù)存取操作,所以源碼中采用一個(gè)static的內(nèi)存來(lái)將YYDiskCache變量存儲(chǔ)起來(lái):
static NSMapTable *_globalInstances;
static dispatch_semaphore_t _globalInstancesLock;
_globalInstancesLock
是保證線程安全的信號(hào)量锌仅。
初始化方法和_globalInstances
的創(chuàng)建不再解釋直接看源碼很容易理解章钾,YYDiskCache中只有三個(gè)變量:
//負(fù)責(zé)操作數(shù)據(jù)的類墙贱,里面實(shí)現(xiàn)了全部的數(shù)據(jù)讀寫(xiě)方法和LRU淘汰算法
YYKVStorage *_kv;
//線程安全的信號(hào)量
dispatch_semaphore_t _lock;
//異步線程隊(duì)列
dispatch_queue_t _queue;
數(shù)據(jù)庫(kù)模型對(duì)象:
@interface YYKVStorageItem : NSObject
@property (nonatomic, strong) NSString *key; ///< key
@property (nonatomic, strong) NSData *value; ///< value
@property (nullable, nonatomic, strong) NSString *filename; ///< filename (nil if inline)
@property (nonatomic) int size; ///< value's size in bytes
@property (nonatomic) int modTime; ///< modification unix timestamp
@property (nonatomic) int accessTime; ///< last access unix timestamp
@property (nullable, nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
@end
下面解讀存取流程。
存儲(chǔ)流程分析
當(dāng)數(shù)據(jù)來(lái)的時(shí)候首先判斷數(shù)據(jù)的大小和_inlineThreshold
閥值比較如果大于該閥值則表示數(shù)據(jù)要存儲(chǔ)到文件中此時(shí)用一個(gè)變量filename
代表存儲(chǔ)文件的名字贱傀,反之該變量為nil惨撇。YYDiskCache -
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key
if (value.length > _inlineThreshold) {
filename = [self _filenameForKey:key];
}
之后會(huì)調(diào)用YYKVStorage中的方法來(lái)存數(shù)據(jù) -
[_kv saveItemWithKey:key value:value filename:filename extendedData:extendedData]
進(jìn)入到對(duì)應(yīng)方法中會(huì)發(fā)現(xiàn)判斷failName是否為空,如果不為空說(shuō)明需要存儲(chǔ)到文件中府寒,此時(shí)會(huì)調(diào)用_fileWriteWithName
方法存儲(chǔ)到文件魁衙。之后會(huì)還會(huì)存儲(chǔ)到數(shù)據(jù)庫(kù)中,但是只存儲(chǔ)除了數(shù)據(jù)以外的其他部分株搔。便于以后讀取存儲(chǔ)到文件中數(shù)據(jù)的屬性(size剖淀,最后讀取時(shí)間,failName等)纤房。
取數(shù)據(jù)時(shí)會(huì)先從數(shù)據(jù)庫(kù)中獲取到item纵隔,因?yàn)闊o(wú)論是存儲(chǔ)到文件中或者數(shù)據(jù)庫(kù)中的文件,它們的基本信息(key炮姨,fileName捌刮,accessTime,size...)都存儲(chǔ)到了數(shù)據(jù)庫(kù)中,拿到了這些信息就可以拿到數(shù)據(jù)舒岸。調(diào)用方法:getItemForKey
該方法會(huì)刷新數(shù)據(jù)庫(kù)中的accessTime字段代表該數(shù)據(jù)最近被讀取過(guò)绅作。而后面的LRU算法也是根據(jù)該字段來(lái)淘汰數(shù)據(jù)的。
拿到item后根據(jù)fileName字段判斷數(shù)據(jù)存在哪里取出來(lái)object
進(jìn)行解碼吁津。
取item代碼:
YYKVStorageItem *item = [self _dbGetItemWithKey:key excludeInlineData:NO];
if (item) {
[self _dbUpdateAccessTimeWithKey:key];
if (item.filename) {
item.value = [self _fileReadWithName:item.filename];
if (!item.value) {
[self _dbDeleteItemWithKey:key];
item = nil;
}
}
}
解碼:
if (_customUnarchiveBlock) {
object = _customUnarchiveBlock(item.value);
} else {
@try {
object = [NSKeyedUnarchiver unarchiveObjectWithData:item.value];
}
@catch (NSException *exception) {
// nothing to do...
}
}
之后判斷是否有附加數(shù)據(jù)如果有則通過(guò)runtime的方法保存起來(lái):objc_setAssociatedObject
棚蓄。
類中數(shù)據(jù)庫(kù)采用的是iOS內(nèi)置的sqlite3使用起來(lái)比較麻煩,可以將數(shù)據(jù)庫(kù)部分替換成FMDB等第三方庫(kù)碍脏。
LRU緩存淘汰算法實(shí)現(xiàn)
LRU(Least recently used梭依,最近最少使用)算法根據(jù)數(shù)據(jù)的歷史訪問(wèn)記錄來(lái)進(jìn)行淘汰數(shù)據(jù),其核心思想是“如果數(shù)據(jù)最近被訪問(wèn)過(guò)典尾,那么將來(lái)被訪問(wèn)的幾率也更高”役拴。
下面將分為Memory和Disk兩部分解析LRU的實(shí)現(xiàn):
Memory-LRU
在YYMemoryCache對(duì)象創(chuàng)建的時(shí)候會(huì)調(diào)用下面方法:
- (void)_trimRecursively {
__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self _trimInBackground];
[self _trimRecursively];
});
}
該方法很明顯是一個(gè)遞歸方法,無(wú)限的在異步線程循環(huán)調(diào)用钾埂,而_trimInBackground方法就是根據(jù)空間大小河闰,存儲(chǔ)數(shù)量,存儲(chǔ)時(shí)間來(lái)淘汰數(shù)據(jù)的方法褥紫。如下代碼:
- (void)_trimInBackground {
dispatch_async(_queue, ^{
[self _trimToCost:self->_costLimit];
[self _trimToCount:self->_countLimit];
[self _trimToAge:self->_ageLimit];
});
}
下面我們只分析根據(jù)空間大小淘汰數(shù)據(jù)的方法姜性,其他的類似,_trimToCost方法:
首先數(shù)據(jù)讀寫(xiě)需要添加線程安全鎖:
pthread_mutex_lock(&_lock);
pthread_mutex_unlock(&_lock);
如果傳遞過(guò)來(lái)的_costLimit為0則代表全部清除直接調(diào)用
[_lru removeAll]
方法清空內(nèi)存緩存數(shù)據(jù)髓考。否則while循環(huán)移除鏈表尾節(jié)點(diǎn)部念,直到總存儲(chǔ)大小小于_costLimit。代碼如下:
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) {
if (_lru->_totalCost > costLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
其中removeTailNode方法就是移除鏈表尾節(jié)點(diǎn)并將數(shù)據(jù)從字典中移除。
內(nèi)存中的LRU算法至此完成儡炼,總結(jié)下來(lái)就是每次讀取數(shù)據(jù)時(shí)該數(shù)據(jù)都會(huì)移動(dòng)到鏈表的頭部妓湘,所以鏈表的尾節(jié)點(diǎn)就是相對(duì)最近最少使用的數(shù)據(jù)。而開(kāi)頭的無(wú)限遞歸方法_trimRecursively
就是定期淘汰最近最少使用過(guò)的數(shù)據(jù)乌询。
Disk-LRU
上面介紹了Memory中根據(jù)鏈表存儲(chǔ)順序決定了先淘汰哪個(gè)數(shù)據(jù)榜贴,而Disk中則沒(méi)有有序的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ),所以上文講解Disk存儲(chǔ)的時(shí)候提到了所有要存到磁盤的數(shù)據(jù)(無(wú)論是存到文件中或者數(shù)據(jù)庫(kù)中)都會(huì)將基本信息存到數(shù)據(jù)庫(kù)中妹田,其中包括accessTime
字段:最后讀寫(xiě)該數(shù)據(jù)的時(shí)間唬党,所以我們可以根據(jù)該字段判斷哪個(gè)數(shù)據(jù)是最近最少使用過(guò)的數(shù)據(jù)。使用到的sql語(yǔ)句這里不做過(guò)多解釋秆麸。
YYDiskCache類中和YYMemoryCache類中使用LRU的用法基本一致都是在構(gòu)造方法中異步開(kāi)啟一個(gè)無(wú)限循環(huán)執(zhí)行的方法-_trimRecursively
,該方法會(huì)每隔一段時(shí)間進(jìn)行數(shù)據(jù)檢查淘汰:
- (void)_trimInBackground {
__weak typeof(self) _self = self;
dispatch_async(_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
Lock();
[self _trimToCost:self.costLimit];
[self _trimToCount:self.countLimit];
[self _trimToAge:self.ageLimit];
[self _trimToFreeDiskSpace:self.freeDiskSpaceLimit];
Unlock();
});
}
下面只講解一下_trimToCost:
方法的實(shí)現(xiàn)初嘹,其他的條件檢查類似。
首先根據(jù)方法:[self _dbGetTotalItemSize]
獲取到當(dāng)前所存儲(chǔ)的數(shù)據(jù)總大小: total
沮趣,該方法直接將數(shù)據(jù)庫(kù)中所有的size字段累加即可屯烦,因?yàn)闊o(wú)論存儲(chǔ)到哪里的數(shù)據(jù),size屬性都會(huì)保存在數(shù)據(jù)庫(kù)中房铭。將total
和maxSize
進(jìn)行比較驻龟。
然后寫(xiě)一個(gè)do-while循環(huán),循環(huán)的跳出條件就是當(dāng)total
< maxSize
缸匪。
根據(jù)方法:- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeAscWithLimit:(int)count
取出數(shù)據(jù)庫(kù)中accessTime字段最小的count
條數(shù)據(jù)翁狐,sql語(yǔ)句如下:
NSString *sql = @"select key, filename, size from manifest order by last_access_time asc limit ?1;";
該方法返回一個(gè)YYKVStorageItem
對(duì)象數(shù)組,根據(jù)YYKVStorageItem
的fileName屬性可以直到數(shù)據(jù)存儲(chǔ)的位置凌蔬,如果fileName不為nil則存儲(chǔ)到文件中調(diào)用:- (BOOL)_fileDeleteWithName:(NSString *)filename
方法從文件中刪除露懒,在調(diào)用- (BOOL)_dbDeleteItemWithKey:(NSString *)key
方法從數(shù)據(jù)庫(kù)中刪除,如果只存儲(chǔ)到數(shù)據(jù)庫(kù)中則直接調(diào)用后者方法即可砂心。一次根據(jù)存儲(chǔ)大小條件的數(shù)據(jù)淘汰完成懈词。
至此LRU算法解讀完畢,我們可以發(fā)現(xiàn)想要實(shí)現(xiàn)完善的LRU良好的數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理是必須的辩诞,良好的數(shù)據(jù)結(jié)構(gòu)可以提升數(shù)據(jù)讀寫(xiě)效率坎弯,良好的內(nèi)存管理可以最大化節(jié)省CPU和磁盤資源并防止出錯(cuò)。
總結(jié)
首先由衷的感謝和敬佩作者:ibireme译暂,給大家分享這么優(yōu)秀的源碼抠忘,用過(guò)YYKit的開(kāi)發(fā)者應(yīng)該對(duì)ibireme不陌生,他優(yōu)秀的編程思想和代碼設(shè)計(jì)理念值得我們學(xué)習(xí)外永。
本文講解了ibireme的開(kāi)源代碼YYCache崎脉,筆者從中主要學(xué)習(xí)到數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)和里面對(duì)于線程隊(duì)列的處理,以及LRU淘汰算法伯顶。如果想做iOS數(shù)據(jù)緩存YYCache是一個(gè)不錯(cuò)的選擇荧嵌。關(guān)于YYCache的用法大家可以參考這篇文章:YYCache的使用
作者:Olivia_Zqy