YYMemoryCache內存緩存

YYMemoryCache是用來存儲佔用內存小 速度較快的高速內存緩存, 存儲key-value形式的鍵值對.

區別於NSDictionary, YYMemoryCache對key是retain而不是copy操做(YYMemoryCache的key能夠是任何對象的緣由, 內部是使用CFMutableDictionaryRef實現的), 提供的API和NSCache相似, 而且YYMemoryCache的全部方法都是安全的.node

YYMemoryCache相比NSCache的特色:算法

  1. 使用LRU(least-recently-used)淘汰算法(NSCache是清除全部的緩存信息)
  2. 使用cost count age三個維度清除緩存(NSCache值提供了cost count兩個維度)
  3. 自動清除緩存(當收到內存警告時 會在後臺清除緩存)

YYMemoryCache中使用一個雙向鏈表和字典進行增刪改查操做的:數組

  1. 當須要增長新的緩存時, 會生成一個結點並設置爲頭結點
  2. 當須要獲取一個緩存時, 從YYLinkedMap->_dic中經過key獲取到對應的node結點, 並將node結點設置爲頭結點
  3. 當須要刪除一個緩存時, 從YYLinkedMap->_dic中經過key刪除到對應的node結點, 並將node結點從雙向鏈表中刪除
  4. 當須要按需清除緩存時, 從尾結點開始清除

一. YYLinkedMapNode

YYLinkedMapNode能夠理解爲鏈表中的結點. YYMemoryCache中是將傳入的value封裝成node存儲到緩存中的.緩存

@interface _YYLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
    id _key; // key
    id _value; // value
    NSUInteger _cost; // 結點的開銷, 在內存中佔用的字節數
    NSTimeInterval _time; // 操做結點的時間
}
@end

@implementation _YYLinkedMapNode

@end
複製代碼

須要注意的是Node並不持有對_prev和_next的強引用, _prev和_next執行的內容是由_dic強引用安全

二. YYLinkedMap

YYLinkedMap能夠理解爲是一個雙向鏈表, 方便操做頭結點和尾結點.數據結構

@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic; // do not set object directly key: key, value: Node結點
    NSUInteger _totalCost; // 總開銷
    NSUInteger _totalCount; // 緩存數量
    _YYLinkedMapNode *_head; // MRU, do not change it directly 頭結點
    _YYLinkedMapNode *_tail; // LRU, do not change it directly 尾結點
    BOOL _releaseOnMainThread; // 是否在主線程release結點
    BOOL _releaseAsynchronously; // 是否異步release結點
}
複製代碼

思考: 問什麼YYLinkedMap中要使用一個字典呢?多線程

使用字典是爲了使雙向鏈表的查詢某個結點的操做時間複雜度爲O(1), 若是是雙向鏈表獲取某一個結點的話須要遍歷雙向鏈表, 時間複雜度爲O(n)併發

YYLinkedMap中使用CFMutableDictionaryRef建立字典, 而不是使用NSDictionary建立字典的緣由:app

由於NSDictionary的key須要遵循NSCoding協議, 而CFMutableDictionaryRef則不須要, 另外CFMutableDictionaryRef更加靠近底層, 效率更高, 可是建立的_dic的內存須要咱們本身手動回收.異步

YYLinkedMap的接口:

// 在鏈表頭部插入結點
    - (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
    // 將結點移動到鏈表頭部
    - (void)bringNodeToHead:(_YYLinkedMapNode *)node;
    // 移除結點
    - (void)removeNode:(_YYLinkedMapNode *)node;
    // 移除尾結點
    - (_YYLinkedMapNode *)removeTailNode;
    // 清空全部結點
    - (void)removeAll;
複製代碼

YYLinkedMap的操做主要是對雙向鏈表進行的插入 刪除操做, 下圖中虛線爲弱引用, 實線爲強引用

YYLinkedMap的接口實現: 添加結點:

- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    // 將結點node保存在_dic中
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    // 根據結點的cost開銷修改map記錄的總開銷數
    _totalCost += node->_cost;
    // 記錄緩存個數
    _totalCount++;
    if (_head) {
        // 將結點node設置爲頭結點
        node->_next = _head;
        _head->_prev = node;
        _head = node;
    } else {
        // 若是頭結點爲nil, 說明鏈表爲空, 添加的結點node就是頭結點 而且仍是尾結點
        _head = _tail = node;
    }
}
複製代碼

移動結點:

- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    // 若是隻有一個結點, 說明不須要移動
    if (_head == node) return;
    
    if (_tail == node) {
        // 若是node是尾結點, 從新設置尾結點
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        // node既不是頭結點又不是尾結點, 至關於刪除結點node
        node->_next->_prev = node->_prev;
        node->_prev->_next = node->_next;
    }
    // 將node設置爲頭結點
    node->_next = _head;
    node->_prev = nil;
    _head->_prev = node;
    _head = node;
}
複製代碼

刪除結點:

- (void)removeNode:(_YYLinkedMapNode *)node {
    // 將結點node從字典_dic中移除, 注意這是node的引用計數減1
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    // 修改總開銷
    _totalCost -= node->_cost;
    // 緩存數量減1
    _totalCount--;
    // 修改node結點的下一個結點prev指向
    if (node->_next) node->_next->_prev = node->_prev;
    // 修改node結點的上一個結點的next指向
    if (node->_prev) node->_prev->_next = node->_next;
    // 若是node是頭結點, 將頭結點設置爲node的下一個結點
    if (_head == node) _head = node->_next;
    // 若是node是尾結點, 將尾結點設置爲node的上一個結點
    if (_tail == node) _tail = node->_prev;
}
複製代碼

注意這裏的結點操做執行的順序, 考慮node是頭結點或者尾結點 以及爲中間結點的處理狀況. 移除尾結點:

- (_YYLinkedMapNode *)removeTailNode {
    if (!_tail) return nil;
    // 獲取尾結點
    _YYLinkedMapNode *tail = _tail;
    // 將尾結點從字典中移除, 由於字典有強引用
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
    _totalCost -= _tail->_cost;
    _totalCount--;
    if (_head == _tail) {
        // 若是頭結點=尾結點, 移除以後鏈表爲空
        _head = _tail = nil;
    } else {
        // 否者重置尾結點
        _tail = _tail->_prev;
        _tail->_next = nil;
    }
    return tail;
}	
複製代碼

清空數據:

- (void)removeAll {
    // 清空信息
    _totalCost = 0;
    _totalCount = 0;
    _head = nil;
    _tail = nil;
    if (CFDictionaryGetCount(_dic) > 0) {
        // 至關於對_dic進行了一次mutableCopy, 因爲_dic是不可變, 因此holder和_dic執行了同一塊內存空間(堆空間)
        CFMutableDictionaryRef holder = _dic;
        // 從新在堆空間申請內存, _dic執行新分配的內存(以前堆空間的內存地址保存在holder中)
        _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        // 若是須要異步執行, 不阻塞當前線程
        if (_releaseAsynchronously) {
            dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else if (_releaseOnMainThread && !pthread_main_np()) {
            // 主線程執行
            dispatch_async(dispatch_get_main_queue(), ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else {
            // 主線程執行
            CFRelease(holder);
        }
    }
}
複製代碼

清空數據這裏做者的操做很巧妙, 利用holder引用dic, 從新給dic申請堆空間, 在指定的隊列中實現對holder的release操做.

三. YYMemoryCache

YYMemoryCache是對外提供的類, 提供了一些配置屬性和外界訪問的方法.

1. 屬性和方法

@interface YYMemoryCache : NSObject
// 緩存名稱
@property (nullable, copy) NSString *name;

// 緩存的總數量
@property (readonly) NSUInteger totalCount;

// 緩存的總開銷
@property (readonly) NSUInteger totalCost;

// 緩存的數量限制, 默認值是NSUIntegerMax表示不作限制
@property NSUInteger countLimit;

// 緩存的開銷限制, 默認值是NSUIntegerMax表示不作限制, 例如設置緩存總開銷爲200k, 那麼當總開銷大於200k時就會在後臺自動清理
@property NSUInteger costLimit;

// 緩存的時間限制, 默認是DBL_MAX表示不限制
@property NSTimeInterval ageLimit;

// 自動清理時間設置, 默認值是5s, 也就是5秒鐘會自動檢查清理緩存
@property NSTimeInterval autoTrimInterval;

// 當收到內存警告時, 是否清除全部緩存, 默認是YES
@property BOOL shouldRemoveAllObjectsOnMemoryWarning;

// 當App進入後臺時, 是否清除全部緩存, 默認是YES
@property BOOL shouldRemoveAllObjectsWhenEnteringBackground;

// 收到內存警告時執行的block
@property (nullable, copy) void(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache);

// 進入後臺時執行的block
@property (nullable, copy) void(^didEnterBackgroundBlock)(YYMemoryCache *cache);

// 當須要移除某個value時, release操做是否在主線程執行, 默認爲NO
@property BOOL releaseOnMainThread;

// 當須要移除某個value時, release造做是否異步執行, 默認爲YES
@property BOOL releaseAsynchronously;

// 緩存中是否存在key
- (BOOL)containsObjectForKey:(id)key;

// 從緩存中獲取和key相關聯的value
- (nullable id)objectForKey:(id)key;

// 緩存key-value, 若是value=nil, 表示移除key的緩存
- (void)setObject:(nullable id)object forKey:(id)key;

// 緩存key-value, 指定開銷
- (void)setObject:(nullable id)object forKey:(id)key withCost:(NSUInteger)cost;

// 從緩存中移除key
- (void)removeObjectForKey:(id)key;

// 清空緩存
- (void)removeAllObjects;

// 將緩存修剪到指定的數量, 從鏈表的尾結點移除
- (void)trimToCount:(NSUInteger)count
    
// 將緩存修剪到指定的開銷, 從鏈表的尾結點移除
- (void)trimToCost:(NSUInteger)cost;

// 將緩存修剪到指定的時間, 大於這個時間的數據都會被清除
- (void)trimToAge:(NSTimeInterval)age;
@end
複製代碼

2. 初始化配置

在YYMemoryCahce的初始化方法中, 對一些屬性進行了默認參數配置

@implementation YYMemoryCache {
    pthread_mutex_t _lock; // 互斥鎖
    _YYLinkedMap *_lru; // least recent used
    dispatch_queue_t _queue; // 串行隊列
}

- (instancetype)init {
    self = super.init;
    // 初始化鎖
    pthread_mutex_init(&_lock, NULL);
    // 初始化雙向鏈表
    _lru = [_YYLinkedMap new];
    // 串行隊列, 用於執行修剪操做, 爲何是串行隊列?
    _queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
    
    _countLimit = NSUIntegerMax;
    _costLimit = NSUIntegerMax;
    _ageLimit = DBL_MAX;
    _autoTrimInterval = 5.0;
    _shouldRemoveAllObjectsOnMemoryWarning = YES;
    _shouldRemoveAllObjectsWhenEnteringBackground = YES;
    
    // 註冊App的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
    
    // 自動清除功能
    [self _trimRecursively];
    return self;
}
複製代碼

3. 自動清理緩存

在init方法中調用了_trimRecursively方法.

- (void)_trimRecursively {
    // 使用weakself, block不會對self進行強引用
    __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修飾, 防止self被提早銷燬
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _trimInBackground];
        [self _trimRecursively];
    });
}

- (void)_trimInBackground {
    // 爲何是串行隊列? 串行隊列中的任務會等待上個任務執行完成之後才執行, 是想按照cost->count->age的順序去清除緩存
    dispatch_async(_queue, ^{
        [self _trimToCost:self->_costLimit];
        [self _trimToCount:self->_countLimit];
        [self _trimToAge:self->_ageLimit];
    });
}
複製代碼

_trimRecursively方法中使用一個dispatch_after進行延時操做, 延時時間就是屬性autoTrimInterval, 經過延時遞歸調用_trimRecursively方法實現自動清理緩存的功能.

_trimRecursively中爲何使用__weak__strong去修飾self呢?

當不使用__weak__strong修飾self的時候, demo以下:

@interface MyObject : NSObject
@end
@implementation MyObject
- (void)_trimRecursively {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSLog(@"1-----");
        if (!self) return; // block會捕獲self, copy, self引用計數加一.
        [self _trimRecursively]; // 由於這裏是異步調用, 不會阻塞當前線程(遞歸調用方法中又會重複對self進行 copy和release操做)
        NSLog(@"2-----"); // 執行完block之後, block釋放對self的引用, release, self引用計數減一.
    });
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    MyObject *obj = [[MyObject alloc] init];
    [obj _trimRecursively];
}

@end
複製代碼

viewDidLoad執行完成之後, obj就應該被釋放, 可是在_trimRecursively的block中一直存在對obj的強引用, 因此就形成了obj永遠不會被釋放.

4. 手動清理緩存

_trimToCost _trimToCount _trimToAge三種清理緩存的方法處理是同樣的, 只是清除緩存的依據不同, 因此這裏只分析了一種狀況.

// static修飾能夠在多個文件中定義
// inline內聯函數, 提升效率, 在調用的時候是在調用出直接替換, 而普通的函數須要開闢棧空間(push pop操做)
static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}
複製代碼
- (void)_trimToCost:(NSUInteger)costLimit {
    BOOL finish = NO;
    // 避免多線程訪問出現數據錯亂, 因此選擇加鎖
    pthread_mutex_lock(&_lock);
    if (costLimit == 0) {
        // 若是內存開銷設置爲0, 表示清空緩存
        [_lru removeAll];
        finish = YES;
    } else if (_lru->_totalCost <= costLimit) {
        // 若是當前緩存的開銷小於指定限制開銷, 不須要清理
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    // 建立一個數組, 數組中存放的是結點, 爲了在指定的線程 異步或者同步release
    NSMutableArray *holder = [NSMutableArray new];
    // 這裏的時間複雜度是O(n)???
    while (!finish) {
        // 嘗試加鎖, 若是成功就爲0
        if (pthread_mutex_trylock(&_lock) == 0) {
            // 將須要移除的結點添加到數組中
            if (_lru->_totalCost > costLimit) {
                // 這裏node雖然從dic字典和雙向鏈表中移除了, 可是又加入了數組中, 因此這裏node並不會被銷燬
                _YYLinkedMapNode *node = [_lru removeTailNode];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            // 爲了防止爭奪資源, sleep10ms
            usleep(10 * 1000); //10 ms
        }
    }
    // holder中保存了須要release的node結點
    if (holder.count) {
        // 根據配置選擇是否在主線程中release結點資源
        // YYMemoryCacheGetReleaseQueue()是內聯函數, 建立的全局併發隊列
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
        // 在隊列中移除
        dispatch_async(queue, ^{
            // 這裏個人理解是 block會對holder強引用, 當block執行完成之後holder的引用計數減1
            [holder count]; // release in queue
        });
    }
    // 由於block中對holder有強引用, 因此出了}之後, holder的引用計數減1, 不會被銷燬, 而是在block中銷燬(實如今指定的隊列中銷燬).
}
複製代碼

5. 屬性的setter方法

YYMemoryCache中提供了一些配置屬性, 經過重寫setter方法, 實現對YYLinkedMap的配置

- (NSUInteger)totalCount {
    pthread_mutex_lock(&_lock);
    NSUInteger count = _lru->_totalCount;
    pthread_mutex_unlock(&_lock);
    return count;
}

- (NSUInteger)totalCost {
    pthread_mutex_lock(&_lock);
    NSUInteger totalCost = _lru->_totalCost;
    pthread_mutex_unlock(&_lock);
    return totalCost;
}

- (BOOL)releaseOnMainThread {
    pthread_mutex_lock(&_lock);
    BOOL releaseOnMainThread = _lru->_releaseOnMainThread;
    pthread_mutex_unlock(&_lock);
    return releaseOnMainThread;
}

- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread {
    pthread_mutex_lock(&_lock);
    _lru->_releaseOnMainThread = releaseOnMainThread;
    pthread_mutex_unlock(&_lock);
}

- (BOOL)releaseAsynchronously {
    pthread_mutex_lock(&_lock);
    BOOL releaseAsynchronously = _lru->_releaseAsynchronously;
    pthread_mutex_unlock(&_lock);
    return releaseAsynchronously;
}
複製代碼

6. 增刪改查操做

查找:

- (BOOL)containsObjectForKey:(id)key {
    if (!key) return NO;
    // 由於須要操做map中的數據, 加鎖
    pthread_mutex_lock(&_lock);
    BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
    pthread_mutex_unlock(&_lock);
    return contains;
}

- (id)objectForKey:(id)key {
    if (!key) return nil;
    pthread_mutex_lock(&_lock);
    // 先從字典中獲取node
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        // 由於操做了node, 因此須要將node的時間修改爲當前時間
        node->_time = CACurrentMediaTime();
        // 操做告終點, 將結點設置爲頭結點.
        [_lru bringNodeToHead:node];
    }
    pthread_mutex_unlock(&_lock);
    return node ? node->_value : nil;
}
複製代碼

增長:

- (void)setObject:(id)object forKey:(id)key {
    [self setObject:object forKey:key withCost:0];
}

- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
    if (!key) return;
    // 若是object爲nil, 說明是移除key
    if (!object) {
        [self removeObjectForKey:key];
        return;
    }
    pthread_mutex_lock(&_lock);
    // 將node存儲在字典中
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    // 獲取當前時間, 用於設置node的操做時間
    NSTimeInterval now = CACurrentMediaTime();
    if (node) {
        // 若是node已經存在緩存中, 須要將node的time修改, 而且將node添加到表頭
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        [_lru bringNodeToHead:node];
    } else {
        // 若是node不存在, 須要建立node, 而且添加到表頭
        node = [_YYLinkedMapNode new];
        node->_cost = cost;
        node->_time = now;
        node->_key = key;
        node->_value = object;
        [_lru insertNodeAtHead:node];
    }
    // 添加完成之後, 由於_totalCost增長, 須要檢查總開銷是否超過了設定的標準_costLimit
    if (_lru->_totalCost > _costLimit) {
        // 在串行隊列中異步清除緩存
        dispatch_async(_queue, ^{
            [self trimToCost:_costLimit];
        });
    }
    // 添加完成之後, 由於_totalCount增長, 須要檢查_totalCount是否大於_countLimit
    if (_lru->_totalCount > _countLimit) {
        // 由於每次只增長一個node, 因此只須要移除最後一個尾結點便可
        _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);
}
複製代碼

刪除:

- (void)removeObjectForKey:(id)key {
    if (!key) return;
    pthread_mutex_lock(&_lock);
    // 獲取須要移除的node
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        [_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);
}

- (void)removeAllObjects {
    pthread_mutex_lock(&_lock);
    [_lru removeAll];
    pthread_mutex_unlock(&_lock);
}
複製代碼

7. 其餘

- (NSString *)description {
    if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
    else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
}
複製代碼

重寫description方法, 當log YYMemoryCache的對象時就會調用這個方法, log相關信息.

四. 思考

1. MRU LRU 淘汰算法?

LRU(least-recently-used): 最近最少使用, 是一種淘汰算法, 若是最近模塊數據被訪問, 那麼未來這塊數據被訪問的機率也很大. 最久沒有被訪問的數據, 未來被訪問的機率會很小. 將最久沒有使用的淘汰掉, 須要使用一個time字段, 記錄每次訪問的時間, 當須要淘汰的時候, 就選擇time最大的淘汰.

MRU(most-recently-used): 最近最常使用, 和LRU相反.

2. YYMemoryCache是如何保證線程安全的?

YYMemoryCache須要操做資源時加鎖. Pthread_mutex鎖.

YYMemoryCache中對清除緩存的操做 訪問_YYLinkedMap中的數據 以及getter setter方法都加鎖處理, 避免多線程訪問的時候出現數據錯亂.

3. YYMemoryCache是從哪幾個方面提升性能的?

  • 選擇合適的數據結構, 減小時間複雜度, YYMemoryCache中是使用雙向鏈表+字典, 雙向鏈表方便操做頭結點和尾結點, 方便查找和修改中間結點的上一個和下一個節點, 操做的時間複雜度爲O(1), 而雙向鏈表的查找複雜度爲O(n). 字典的查找複雜度爲O(1), YYMemoryCache使用雙向鏈表+字典的數據結構, 增刪改查操做的時間複雜度都爲O(1).
  • 清除緩存的操做異步執行, release對象異步執行

4. dispatch_semaphore和Pthread_mutex鎖, 性能對比 如何選擇?

// dispatch_semaphore是信號量, 當信號量爲1時也能夠當作鎖來使用, 當信號量小於1時就會等待
dispatch_semaphore_t lock = dispatch_semaphore_create(1);
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
...
dispatch_semaphore_signal(lock);

pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
...
pthread_mutex_unlock(&lock);
複製代碼

當沒有等待的狀況下, dispatch_semaphore的性能要比pthread_mutext高, 可是一旦出現等待的狀況, 性能就會降低不少.

相關文章
相關標籤/搜索