YYCache源碼解讀

YYCache是一個(gè)線程安全的高性能鍵值緩存組件沦补,代碼質(zhì)量很高,他的作者是國內(nèi)開發(fā)者ibireme開發(fā)并且開源的. 下面就來簡(jiǎn)單剖析一下他的源碼.如有哪里錯(cuò)誤希望拍磚!多多交流多多進(jìn)步!謝謝!!!

基本使用方法


//要緩存的對(duì)象
NSString*name =@"JJCoderMa";
//需要緩存的對(duì)象在緩存里對(duì)應(yīng)的鍵
NSString *key =@"username";
//創(chuàng)建一個(gè)YYCache實(shí)例:userInfoCache
YYCache *userInfoCache = [YYCache cacheWithName:@"userInfocache"];
//存入鍵值對(duì)
[userInfoCache setObject:userName forKey:key withBlock:^{
    NSLog(@"caching object succeed");
}];

//判斷緩存是否存在
[userInfoCache containsObjectForKey:key withBlock:^(NSString* _Nonnull key,BOOLcontains) {
  if(contains){
    NSLog(@"存在!");
  }
}];

//根據(jù)key讀取數(shù)據(jù)
[userInfoCache objectForKey:key withBlock:^(NSString* _Nonnull key,id  _Nonnull object) {
  NSLog(@"user_name : %@",object);
}];

//根據(jù)key移除緩存
[userInfoCache removeObjectForKey:key withBlock:^(NSString* _Nonnull key) {
    NSLog(@"remove user name %@",key);
}];

//移除所有緩存
[userInfoCache removeAllObjectsWithBlock:^{
    NSLog(@"removing all cache succeed");
}];

//移除所有緩存帶進(jìn)度
[userInfoCache removeAllObjectsWithProgressBlock:^(intremovedCount,inttotalCount) {
    NSLog(@"remove all cache objects: removedCount :%d  totalCount : %d",removedCount,totalCount);
} endBlock:^(BOOLerror) {
if(!error){
    NSLog(@"remove all cache objects: succeed");
}else{
      NSLog(@"remove all cache objects: failed");
}
}];

```swift

YYCache 架構(gòu)圖: (第一次嘗試使用MindNode,圖畫的不好~~~~)

yycache.png
  • YYCache:提供了最外層的接口拔妥,調(diào)用了YYMemoryCache與YYDiskCache的相關(guān)方法。
  • YYMemoryCache:負(fù)責(zé)處理容量小旬牲,相對(duì)高速的內(nèi)存緩存。線程安全,支持自動(dòng)和手動(dòng)清理緩存等功能澡绩。
  • _YYLinkedMap:YYMemoryCache使用的雙向鏈表類列疗。
  • _YYLinkedMapNode:是_YYLinkedMap使用的節(jié)點(diǎn)類滑蚯。
  • YYDiskCache:負(fù)責(zé)處理容量大,相對(duì)低速的磁盤緩存抵栈。線程安全告材,支持異步操作,自動(dòng)和手動(dòng)清理緩存等功能古劲。
  • YYKVStorage:YYDiskCache的底層實(shí)現(xiàn)類斥赋,用于管理磁盤緩存。
  • YYKVStorageItem:內(nèi)置在YYKVStorage中产艾,是YYKVStorage內(nèi)部用于封裝某個(gè)緩存的類
YYCache給用戶提供所有最外層的緩存操作接口灿渴,而這些接口的內(nèi)部?jī)?nèi)部實(shí)際上是調(diào)用了YYMemoryCache和YYDiskCache對(duì)象的相關(guān)方法。

因?yàn)閅YMemoryCache和YYDiskCache的實(shí)例作為YYCache的兩個(gè)公開的屬性胰舆,所以用戶無法直接使用YYMemoryCache和YYDiskCache對(duì)象骚露,只能通過屬性的方式來間接使用它們。

YYCache的部分屬性和接口

interface.png

YYCache的接口實(shí)現(xiàn)

imp.png

從上面的接口實(shí)現(xiàn)可以看出:在YYCache中缚窿,永遠(yuǎn)都是先訪問內(nèi)存緩存鬼佣,然后再訪問磁盤緩存(包括了寫入翔试,讀取,查詢晒夹,刪除緩存的操作)。而且關(guān)于內(nèi)存緩存(_memoryCache)的操作婉烟,是不存在block回調(diào)的

YYMemoryCache

YYMemoryCache 操作類似于NSCache,它將需要緩存的對(duì)象與傳入的key關(guān)聯(lián)起來。
YYMemoryCache的內(nèi)部有:
緩存淘汰算法:使用LRU(least-recently-used) 算法來淘汰(清理)使用頻率較低的緩存。
緩存清理策略:使用三個(gè)維度來標(biāo)記育瓜,分別是count(緩存數(shù)量),cost(開銷)栽烂,age(距上一次的訪問時(shí)間)躏仇。YYMemoryCache提供了分別針對(duì)這三個(gè)維度的清理緩存的接口。用戶可以根據(jù)不同的需求(策略)來清理在某一維度超標(biāo)的緩存腺办。
緩存淘汰算法的目的在于區(qū)分出使用頻率高和使用頻率低的緩存焰手,當(dāng)緩存數(shù)量達(dá)到一定限制的時(shí)候會(huì)優(yōu)先清理那些使用頻率低的緩存。因?yàn)槭褂妙l率已經(jīng)比較低的緩存在將來的使用頻率也很有可能會(huì)低怀喉。

YYMemoryCache用一個(gè)鏈表節(jié)點(diǎn)類來保存某個(gè)單獨(dú)的內(nèi)存緩存的信息(鍵书妻,值,緩存時(shí)間等)躬拢,然后用一個(gè)雙向鏈表類來保存和管理這些節(jié)點(diǎn)躲履。這兩個(gè)類的名稱分別是:
_YYLinkedMapNode:鏈表內(nèi)的節(jié)點(diǎn)類,可以看做是對(duì)某個(gè)單獨(dú)內(nèi)存緩存的封裝聊闯。
_YYLinkedMap:雙向鏈表類工猜,用于保存和管理所有內(nèi)存緩存(節(jié)點(diǎn))
_YYLinkedMapNode可以被看做是對(duì)某個(gè)緩存的封裝:它包含了該節(jié)點(diǎn)上一個(gè)和下一個(gè)節(jié)點(diǎn)的指針,以及緩存的key
和對(duì)應(yīng)的值(對(duì)象)馅袁,還有該緩存的開銷和訪問時(shí)間域慷。

/**
 A node in linked map.
 Typically, you should not use this class directly.
 鏈表內(nèi)的節(jié)點(diǎn)類,可以看做是對(duì)某個(gè)單獨(dú)內(nèi)存緩存的封裝
 */
@interface _YYLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
    id _key;
    id _value;
    NSUInteger _cost;
    NSTimeInterval _time;
}
@end

_YYLinkedMap:

@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic;    // 用于存放節(jié)點(diǎn)
    NSUInteger _totalCost;          //總開銷
    NSUInteger _totalCount;         //節(jié)點(diǎn)總數(shù)
    _YYLinkedMapNode *_head;            // 鏈表的頭部結(jié)點(diǎn)
    _YYLinkedMapNode *_tail;        // 鏈表的尾部節(jié)點(diǎn)
    BOOL _releaseOnMainThread;          //是否在主線程釋放汗销,默認(rèn)為NO
    BOOL _releaseAsynchronously;    //是否在子線程釋放犹褒,默認(rèn)為YES
}
//在鏈表頭部插入某節(jié)點(diǎn)
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
//將鏈表內(nèi)部的某個(gè)節(jié)點(diǎn)移到鏈表頭部
- (void)bringNodeToHead:(_YYLinkedMapNode *)node;
//移除某個(gè)節(jié)點(diǎn)
- (void)removeNode:(_YYLinkedMapNode *)node;
//移除鏈表的尾部節(jié)點(diǎn)并返回它
- (_YYLinkedMapNode *)removeTailNode;
//移除所有節(jié)點(diǎn)(默認(rèn)在子線程操作)
- (void)removeAll;
@end

CFMutableDictionaryRef,用于保存節(jié)點(diǎn)的鍵值對(duì)弛针,它還持有了鏈表內(nèi)節(jié)點(diǎn)的總開銷叠骑,總數(shù)量,頭尾節(jié)點(diǎn)等數(shù)據(jù)削茁。

[圖片上傳失敗...(image-875de1-1522245469574)]

_YYLinkedMap實(shí)現(xiàn)細(xì)節(jié):

插入結(jié)點(diǎn)(復(fù)習(xí)雙向鏈表的時(shí)候~~~~)

- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    
    //設(shè)置該node的值
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    
    //增加開銷和總緩存數(shù)量
    _totalCost += node->_cost;
    _totalCount++;
    
    if (_head) {
        
        //如果鏈表內(nèi)已經(jīng)存在頭節(jié)點(diǎn)宙枷,則將這個(gè)頭節(jié)點(diǎn)賦給當(dāng)前節(jié)點(diǎn)的尾指針(原第一個(gè)節(jié)點(diǎn)變成了現(xiàn)第二個(gè)節(jié)點(diǎn))
        node->_next = _head;
        
        //將該節(jié)點(diǎn)賦給現(xiàn)第二個(gè)節(jié)點(diǎn)的頭指針(此時(shí)_head指向的節(jié)點(diǎn)是先第二個(gè)節(jié)點(diǎn))
        _head->_prev = node;
        
        //將該節(jié)點(diǎn)賦給鏈表的頭結(jié)點(diǎn)指針(該節(jié)點(diǎn)變成了現(xiàn)第一個(gè)節(jié)點(diǎn))
        _head = node;
        
    } else {
        
        //如果鏈表內(nèi)沒有頭結(jié)點(diǎn),說明是空鏈表茧跋。說明是第一次插入慰丛,則將鏈表的頭尾節(jié)點(diǎn)都設(shè)置為當(dāng)前節(jié)點(diǎn)
        _head = _tail = node;
    }
}

在雙向鏈表中:

  • 每個(gè)節(jié)點(diǎn)都有兩個(gè)分別指向前后節(jié)點(diǎn)的指針。所以說每個(gè)節(jié)點(diǎn)都知道它前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)是誰瘾杭。
  • 鏈表的頭部節(jié)點(diǎn)指向它前面節(jié)點(diǎn)的指針為空诅病;鏈表尾部節(jié)點(diǎn)指向它后側(cè)節(jié)點(diǎn)的指針也為空

將某個(gè)節(jié)點(diǎn)移動(dòng)到表頭

- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    //如果該節(jié)點(diǎn)已經(jīng)是鏈表頭部節(jié)點(diǎn),則立即返回,不做任何操作
    if (_head == node) return;
    if (_tail == node) {
        //如果該節(jié)點(diǎn)是鏈表的尾部節(jié)點(diǎn)
        //1. 將該節(jié)點(diǎn)的頭指針指向的節(jié)點(diǎn)變成鏈表的尾節(jié)點(diǎn)(將倒數(shù)第二個(gè)節(jié)點(diǎn)變成倒數(shù)第一個(gè)節(jié)點(diǎn)贤笆,即尾部節(jié)點(diǎn))
        _tail = node->_prev;
        //2. 將新的尾部節(jié)點(diǎn)的尾部指針置空
        _tail->_next = nil;
    } else {
        //如果該節(jié)點(diǎn)是鏈表頭部和尾部以外的節(jié)點(diǎn)(中間節(jié)點(diǎn))
        //1. 將該node的頭指針指向的節(jié)點(diǎn)賦給其尾指針指向的節(jié)點(diǎn)的頭指針
        node->_next->_prev = node->_prev;
        //2. 將該node的尾指針指向的節(jié)點(diǎn)賦給其頭指針指向的節(jié)點(diǎn)的尾指針
        node->_prev->_next = node->_next;
    }
    //將原頭節(jié)點(diǎn)賦給該節(jié)點(diǎn)的尾指針(原第一個(gè)節(jié)點(diǎn)變成了現(xiàn)第二個(gè)節(jié)點(diǎn))
    node->_next = _head;
    //將當(dāng)前節(jié)點(diǎn)的頭節(jié)點(diǎn)置空
    node->_prev = nil;
    //將現(xiàn)第二個(gè)節(jié)點(diǎn)的頭結(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)(此時(shí)_head指向的節(jié)點(diǎn)是現(xiàn)第二個(gè)節(jié)點(diǎn))
    _head->_prev = node;
    //將該節(jié)點(diǎn)設(shè)置為鏈表的頭節(jié)點(diǎn)
    _head = node;
}

移除鏈表中的某個(gè)節(jié)點(diǎn):

- (void)removeNode:(_YYLinkedMapNode *)node {
    
    //除去該node的鍵對(duì)應(yīng)的值
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    
    //減去開銷和總緩存數(shù)量
    _totalCost -= node->_cost;
    _totalCount--;
    
    //節(jié)點(diǎn)操作
    //1. 將該node的頭指針指向的節(jié)點(diǎn)賦給其尾指針指向的節(jié)點(diǎn)的頭指針
    if (node->_next) node->_next->_prev = node->_prev;
    
    //2. 將該node的尾指針指向的節(jié)點(diǎn)賦給其頭指針指向的節(jié)點(diǎn)的尾指針
    if (node->_prev) node->_prev->_next = node->_next;
    
    //3. 如果該node就是鏈表的頭結(jié)點(diǎn)蝇棉,則將該node的尾部指針指向的節(jié)點(diǎn)賦給鏈表的頭節(jié)點(diǎn)(第二變成了第一)
    if (_head == node) _head = node->_next;
    
    //4. 如果該node就是鏈表的尾節(jié)點(diǎn),則將該node的頭部指針指向的節(jié)點(diǎn)賦給鏈表的尾節(jié)點(diǎn)(倒數(shù)第二變成了倒數(shù)第一)
    if (_tail == node) _tail = node->_prev;
}

查找/緩存/更新某個(gè)對(duì)象

//是否包含某個(gè)緩存對(duì)象
- (BOOL)containsObjectForKey:(id)key {
    //嘗試從內(nèi)置的字典中獲得緩存對(duì)象
    if (!key) return NO;
    pthread_mutex_lock(&_lock);
    BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
    pthread_mutex_unlock(&_lock);
    return contains;
}
//獲取某個(gè)緩存對(duì)象
- (id)objectForKey:(id)key {
    
    if (!key) return nil;
    
    pthread_mutex_lock(&_lock);
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        //如果節(jié)點(diǎn)存在芥永,則更新它的時(shí)間信息(最后一次訪問的時(shí)間)
        node->_time = CACurrentMediaTime();
        [_lru bringNodeToHead:node];
    }
    pthread_mutex_unlock(&_lock);
    
    return node ? node->_value : nil;
}
//寫入某個(gè)緩存對(duì)象篡殷,開銷默認(rèn)為0
- (void)setObject:(id)object forKey:(id)key {
    [self setObject:object forKey:key withCost:0];
}
//寫入某個(gè)緩存對(duì)象,并存入緩存開銷
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
    
    if (!key) return;
    
    if (!object) {
        [self removeObjectForKey:key];
        return;
    }
    
    pthread_mutex_lock(&_lock);
    
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    NSTimeInterval now = CACurrentMediaTime();
    
    if (node) {
        //如果存在與傳入的key值匹配的node埋涧,則更新該node的value,cost,time板辽,并將這個(gè)node移到鏈表頭部
        
        //更新總cost
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        
        //更新node
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        
        //將node移動(dòng)至鏈表頭部
        [_lru bringNodeToHead:node];
        
    } else {
        
        //如果不存在與傳入的key值匹配的node,則新建一個(gè)node飞袋,將key,value,cost,time賦給它戳气,并將這個(gè)node插入到鏈表頭部
        //新建node,并賦值
        node = [_YYLinkedMapNode new];
        node->_cost = cost;
        node->_time = now;
        node->_key = key;
        node->_value = object;
        
        //將node插入至鏈表頭部
        [_lru insertNodeAtHead:node];
    }
    
    //如果cost超過了限制链患,則進(jìn)行刪除緩存操作(從鏈表尾部開始刪除巧鸭,直到符合限制要求)
    if (_lru->_totalCost > _costLimit) {
        dispatch_async(_queue, ^{
            [self trimToCost:_costLimit];
        });
    }
    
    //如果total count超過了限制,則進(jìn)行刪除緩存操作(從鏈表尾部開始刪除麻捻,刪除一次即可)
    if (_lru->_totalCount > _countLimit) {
        _YYLinkedMapNode *node = [_lru removeTailNode];
        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
            });
        }
    }
    pthread_mutex_unlock(&_lock);
}
//移除某個(gè)緩存對(duì)象
- (void)removeObjectForKey:(id)key {
    
    if (!key) return;
    
    pthread_mutex_lock(&_lock);
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
    
        //內(nèi)部調(diào)用了鏈表的removeNode:方法
        [_lru removeNode:node];
        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
            });
        }
    }
    pthread_mutex_unlock(&_lock);
}
//內(nèi)部調(diào)用了鏈表的removeAll方法
- (void)removeAllObjects {
    pthread_mutex_lock(&_lock);
    [_lru removeAll];
    pthread_mutex_unlock(&_lock);
}

YYDiskCache

YYDiskCache處理容量大纲仍,相對(duì)低速的磁盤緩存。線程安全贸毕,支持異步操作郑叠。作為YYCache的第二級(jí)緩存,

與第一級(jí)緩存YYMemoryCache的相同點(diǎn)是:

  • 都具有查詢明棍,寫入乡革,讀取,刪除緩存的接口摊腋。
  • 不直接操作緩存沸版,也是間接地通過另一個(gè)類(YYKVStorage)來操作緩存。
  • 它使用LRU算法來清理緩存兴蒸。
  • 支持按 cost视粮,count 和 age 這三個(gè)維度來清理不符合標(biāo)準(zhǔn)的緩存。

它與YYMemoryCache不同點(diǎn)是:

  • 根據(jù)緩存數(shù)據(jù)的大小來采取不同的形式的緩存:
  • 數(shù)據(jù)庫sqlite: 針對(duì)小容量緩存橙凳,緩存的data和元數(shù)據(jù)都保存在數(shù)據(jù)庫里蕾殴。
  • 除了 cost,count 和 age 三個(gè)維度之外岛啸,還添加了一個(gè)磁盤容量的維度钓觉。
// 緩存方式
typedef NS_ENUM(NSUInteger, YYKVStorageType) {
    YYKVStorageTypeFile = 0,
    YYKVStorageTypeSQLite = 1,
    YYKVStorageTypeMixed = 2,
};

緩存的大致邏輯

  1. 首先判斷傳入的key和value是否符合要求,如果不符合要求坚踩,則立即返回NO荡灾,緩存失敗。
  2. 再判斷是否type==YYKVStorageTypeFile并且文件名為空字符串(或nil):如果是,則立即返回NO卧晓,緩存失敗芬首。

判斷filename是否為空字符串:

  1. 如果不為空:
    寫入文件,并將緩存的key逼裆,等信息寫入數(shù)據(jù)庫郁稍,但是不將key對(duì)應(yīng)的data寫入數(shù)據(jù)庫。
  2. 如果為空:
    如果緩存類型為YYKVStorageTypeSQLite:將緩存文件刪除
    如果緩存類型不為YYKVStorageTypeSQLite:則將緩存的key和對(duì)應(yīng)的data等其他信息存入數(shù)據(jù)庫胜宇。

YYKVStorage

YYKVStorage實(shí)例負(fù)責(zé)保存和管理所有磁盤緩存耀怜。和YYMemoryCache里面的_YYLinkedMap將緩存封裝成節(jié)點(diǎn)類_YYLinkedMapNode類似,YYKVStorage也將某個(gè)單獨(dú)的磁盤緩存封裝成了一個(gè)類桐愉,這個(gè)類就是YYKVStorageItem财破,它保存了某個(gè)緩存所對(duì)應(yīng)的一些信息(key, value, 文件名,大小等等):

YYKVStorageItem結(jié)構(gòu)包含了鍵/值/文件名/值大小/時(shí)間戳/擴(kuò)展數(shù)據(jù)等等字段

/**
 YYKVStorageItem is used by `YYKVStorage` to store key-value pair and meta data.
 Typically, you should not use this class directly.
 YYKVStorage也將某個(gè)單獨(dú)的磁盤緩存封裝成了一個(gè)類从诲,這個(gè)類就是YYKVStorageItem左痢,它保存了某個(gè)緩存所對(duì)應(yīng)的一些信息(key, value, 文件名,大小等等)
 */
@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

還有一些方法來操作數(shù)據(jù)

//YYKVStorage.h
- (BOOL)saveItem:(YYKVStorageItem *)item;
- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value;
- (BOOL)saveItemWithKey:(NSString *)key
                  value:(NSData *)value
               filename:(nullable NSString *)filename
           extendedData:(nullable NSData *)extendedData;
#pragma mark - Remove Items
- (BOOL)removeItemForKey:(NSString *)key;
- (BOOL)removeItemForKeys:(NSArray<NSString *> *)keys;
- (BOOL)removeItemsLargerThanSize:(int)size;
- (BOOL)removeItemsEarlierThanTime:(int)time;
- (BOOL)removeItemsToFitSize:(int)maxSize;
- (BOOL)removeItemsToFitCount:(int)maxCount;

總結(jié)一下:

1. 為什么使用雙向鏈表?
  1. 雙向鏈表可以知道前后節(jié)點(diǎn),所以如果想移動(dòng)其中一個(gè)節(jié)點(diǎn)的話系洛,其前后的節(jié)點(diǎn)不好做銜接俊性。
  2. 其節(jié)點(diǎn)的關(guān)聯(lián)僅僅是靠指針,所以對(duì)于插入和刪除操作會(huì)很便利描扯,而類似數(shù)組的方式尋址操作缺比較費(fèi)時(shí)定页。由于在LRU策略中會(huì)有非常多的移動(dòng),插入和刪除節(jié)點(diǎn)的操作绽诚,使用雙向鏈表是比較有優(yōu)勢(shì)的典徊。
2. 使用CFDictionary而沒有用NSDictionary來實(shí)現(xiàn)?

CFDictionary 更加底層,更快.

3. 為什么內(nèi)存緩存使用互斥鎖?而磁盤緩存使用信號(hào)量?

作者在最初使用的是自旋鎖(OSSpinLock)作為內(nèi)存緩存的線程鎖恩够,但是后來得知其不夠安全卒落,所以退而求其次,使用了pthread_mutex 這篇文章有說明
網(wǎng)上說:但當(dāng)信號(hào)總量設(shè)為 1 時(shí)也可以當(dāng)作鎖來玫鸟。在沒有等待情況出現(xiàn)時(shí)导绷,它的性能比 pthread_mutex 還要高,但一旦有等待情況出現(xiàn)時(shí)屎飘,性能就會(huì)下降許多妥曲。相對(duì)于 OSSpinLock 來說,它的優(yōu)勢(shì)在于等待時(shí)不會(huì)消耗 CPU 資源钦购。對(duì)磁盤緩存來說檐盟,它比較合適。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末押桃,一起剝皮案震驚了整個(gè)濱河市葵萎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖羡忘,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎痢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡卷雕,警方通過查閱死者的電腦和手機(jī)节猿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漫雕,“玉大人滨嘱,你說我怎么就攤上這事〗洌” “怎么了太雨?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)魁蒜。 經(jīng)常有香客問我囊扳,道長(zhǎng),這世上最難降的妖魔是什么梅惯? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任宪拥,我火速辦了婚禮仿野,結(jié)果婚禮上铣减,老公的妹妹穿的比我還像新娘。我一直安慰自己脚作,他們只是感情好葫哗,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著球涛,像睡著了一般劣针。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亿扁,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天捺典,我揣著相機(jī)與錄音,去河邊找鬼从祝。 笑死襟己,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的牍陌。 我是一名探鬼主播擎浴,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼毒涧!你這毒婦竟也來了贮预?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仿吞,沒想到半個(gè)月后滑频,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唤冈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年误趴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片务傲。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凉当,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出售葡,到底是詐尸還是另有隱情看杭,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布挟伙,位于F島的核電站楼雹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏尖阔。R本人自食惡果不足惜贮缅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望介却。 院中可真熱鬧谴供,春花似錦、人聲如沸齿坷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽永淌。三九已至崎场,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遂蛀,已是汗流浹背谭跨。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留李滴,地道東北人螃宙。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悬嗓,于是被迫代替她去往敵國和親污呼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一個(gè)人靜靜在陽臺(tái)上包竹,兩手雙靠攏燕酷,下巴放在手臂上籍凝,看著這一片綠色的花草樹木,心里很平靜苗缩。 寂靜的晚上一個(gè)人仰望星空饵蒂,...
    周心愿閱讀 312評(píng)論 0 0
  • 背景 在日常開發(fā)的一些業(yè)務(wù)場(chǎng)景中,如果涉及到一些敏感信息(如:付款的二維碼或條形碼等)酱讶,而我們不希望相關(guān)敏感信息被...
    Daved閱讀 27,159評(píng)論 11 18
  • 2017~11~16 信陽市七中 學(xué)校教育是“葉”的教育退盯,家庭教育才是“根”的教育。真正的家庭教育是...
    善默勤容閱讀 637評(píng)論 0 0