YYMemoryCache刨根问底

前言

YYMemoryCache缓存内部用双向链表和 NSDictionary 实现了 LRU 淘汰算法,使用pthread_mutex来保证线程安全 ,解析源码之前,先了解一下相关内存置换算法:

  • 最不经常使用算法(LFU):这个缓存算法使用一个计数器来记录条目被访问的频率。通过使用LFU缓存算法,最低访问数的条目首先被移除。这个方法并不经常使用,因为它无法对一个拥有最初高访问率之后长时间没有被访问的条目缓存负责。

  • 最近最少使用算法(LRU):这个缓存算法将最近使用的条目存放到靠近缓存顶部的位置。当一个新条目被访问时,LRU将它放置到缓存的顶部。当缓存达到极限时,较早之前访问的条目将从缓存底部开始被移除。这里会使用到昂贵的算法,而且它需要记录“年龄位”来精确显示条目是何时被访问的。此外,当一个LRU缓存算法删除某个条目后,“年龄位”将随其他条目发生改变(“年龄位”结点的实例变量)

  • 自适应缓存替换算法(ARC):在IBM Almaden研究中心开发,这个缓存算法同时跟踪记录LFU和LRU,以及驱逐缓存条目,来获得可用缓存的最佳使用。

  • 最近最常使用算法(MRU):这个缓存算法最先移除最近最常使用的条目。一个MRU算法擅长处理一个条目越久,越容易被访问的情况。[1]
    First in First out(FIFO)

  • FIFO算法是一个低负载的算法,并且对缓存对象的管理要求不高。通过一个队列去跟踪所有的缓存对象,最近最常用的缓存对象放在后面,而更早的缓存对象放在前面,当缓存容量满时,排在前面的缓存对象会被踢走,然后把新的缓存对象加进去。

  • Second Chance算法是通过FIFO修改而来的,比FIFO好的地方是改善了FIFO的成本.会检查即将要被踢出的对象有没有之前被使用过的标志(1一个 bit表示),没有被使用过把他踢出;否则,把这个标志位清除,然后把这个缓存对象当做新增缓存对象加入队列.可以想象就这就像一个环队列.当再一次在队 头碰到这个对象时,由于已经没有这个标志位了,所以立刻就把对象踢开了.在速度上比FIFO快.

  • simple time-based算法是通过绝对的时间周期去失效那些缓存对象.

  • HTTP新鲜度缓存算法
    http协议有Cache-Control字段来指定请求和响应遵循的缓存机制.如果需要确定响应是保鲜的(fresh)还是陈旧的(stale),只需要将其保鲜寿命(freshness lifetime)和年龄(age)进行比较.

** _YYLinkedMapNode:结点结构:**

@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

** 知识点:**

  • Objective-C中的@package与C语言中变量和函数的private_extern类似。任何在实现类的镜像之外的代码想使用这个实例变量都会引发link error
  • 这个类型最常用于框架类的实例变量,使用@private太限制,使用@protected或者@public又太开放
  • _cost:每个结点的开销,目的是限制链表总资源大小
  • _time:“年龄位”来精确显示条目是何时被访问的,目的修剪过期结点

** _YYLinkedMap:链表结构**

@implementation _YYLinkedMap

- (instancetype)init {
    self = [super init];
    _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    _releaseOnMainThread = NO;
    _releaseAsynchronously = YES;
    return self;
}

- (void)dealloc {
    CFRelease(_dic);
}

// 数据插入到头结点,再把插入的数据作为头结点
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    _totalCost += node->_cost;
    _totalCount++;
        
    
    /** 提供双向循环链表:添加节点思路:这里有两种方法!:
    从前驱线索到后继线索链接:
     head->_prev->_next = node;
     node->_next = head;
     
    
     node->_next = head;
     head->_prev = node;
     
     */
    if (_head) {
        
        // 把插入的数据作为头结点
        node->_next = _head;
        // 设置第二个节点(原先头结点的)的前继
        _head->_prev = node;
        // 设置头节点为插入的节点
        _head = node;
    } else { // 如果插入的是第一个数据
        _head = _tail = node;
    }
}

// node移到head(头结点) 每次缓存命中时将页面转移LRU位置(head)
- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    // 如果该结点是头结点,直接返回
    if (_head == node) return;
    
    
    
    if (_tail == node) {
        // 因为最后一个节点要往(head)头节点移动所以尾节点要指向前一个节点
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        /**  如果该结点位于链表之间
         */
        // 当前结点的前驱线索指向当前结点的后继
        node->_next->_prev = node->_prev;
        // 当前结点的后继线索指向前驱
        node->_prev->_next = node->_next;
    }
    // 当前结点的后继线索指向头结点,那么head为第二结点
    node->_next = _head;
    // 非循环双向链表,将前继线索置为nil
    node->_prev = nil;
    // 此时的head作为第二个结点,第二个结点前驱线索指向第一个节点
    _head->_prev = node;
    // 头结点指针指向第一个节点
    _head = node;
    
    /** 如果是双向循环链表:取消 node->_prev = nil; 这行代码
     head->_prev->_next = node;
     node->_next = head;

     */
}

/// 移除缓存池节点和更新的总成本。节点在已经DIC内。
- (void)removeNode:(_YYLinkedMapNode *)node {
    // 从缓冲池中移除指定对象
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    _totalCost -= node->_cost;
    _totalCount--;
    // 移除节点位于头尾之间
    if (node->_next) node->_next->_prev = node->_prev;
    if (node->_prev) node->_prev->_next = node->_next;
    
    // 移除节点是头节点,头结点指向下一个结点
    if (_head == node) _head = node->_next;
    // 移除节点是尾节点,尾结点指向前一个结点
    if (_tail == node) _tail = node->_prev;
}


- (_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) {
        CFMutableDictionaryRef holder = _dic;
        // 初始化字典
        _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);
        }
    }
}

@end

** 补充知识点 **

  • holder 持有了待释放的对象,这些对象应该根据配置在不同线程进行释放(release)。
  • holder是为了在特定queue中释放

主要思想:

  • 移除尾节点(removeTailNode),实际上这是LRU近期最少使用算法原理,作者在添加数据时对添加数据和再次添加数据进行了处理,如果添加的数据已存在,在链表的位置提前至head(也就是LRU位置),这样可以确保tail位置是最近最少使用的节点,
  • 移除尾节点和更新的总成本和数据量,该类的核心算法,作者在把数据的添加逻辑,以及数据再次添加逻辑都处理过了,所以如果使用的是LRU算法,只需移除尾节点即可,那么如果我们想替换一个算法,就可以从该方法下手,如果用的是MRU算法,移除head结点即可,下面例子是我写的移除Head头结点的方法。

MRU淘汰算法
- (_YYLinkedMapNode *)removeHeadNode {
if (!_head) return nil;
_YYLinkedMapNode *head = _head; // 记录头结点
CFDictionaryRemoveValue(_dic, (_bridge const void *)(head->_key));
// 更新的总成本和数据量
_totalCost -= _head->_cost;
_totalCount--;
if (_head == _tail) {
_head = _tail = nil;
} else {

        // 设置头节点指向后一个节点
        _head = _head->_next;
        // 设置投节点的前驱线索置为nil。
        _head->_prev = nil;
    }
    // 返回头节点 head是原先没有头结点
    return head;
}

YYMemoryCache 内存缓存

@implementation YYMemoryCache {
    pthread_mutex_t _lock;
     // LRU->dic相当于缓存池
    _YYLinkedMap *_lru;
    dispatch_queue_t _queue;
}
// trimRecursively修剪递归
- (void)_trimRecursively {
    // 1.防止Block的循环引用, wself是为了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), ^{
        // 2.防止多线程和arc环境下弱引用提前释放,加上__strong可以保证运行block期间_self不会被释放
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _trimInBackground];
        [self _trimRecursively];
    });
}

- (void)_trimInBackground {
    
    // 使用线程异步执行方式,将block作为一个任务添加到串行队列处理,
    dispatch_async(_queue, ^{
        [self _trimToCost:self->_costLimit];
        [self _trimToCount:self->_countLimit];
        [self _trimToAge:self->_ageLimit];
    });
}

// 修剪Cost
- (void)_trimToCost:(NSUInteger)costLimit {  
    BOOL finish = NO;
    pthread_mutex_lock(&_lock);
    if (costLimit == 0) {
        [_lru removeAll];
        finish = YES;
    } else if (_lru->_totalCost <= costLimit) {
        
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        /**
         函数是pthread_mutex_lock函数的非阻塞版本。如果mutex参数所指定的互斥锁已经被锁定的话,调用pthread_mutex_trylock函数不会阻塞当前线程,而是立即返回一个值来描述互斥锁的状况。
         */
        // pthread_mutex_trylock:非阻塞的锁定互斥锁。函数成功返回0。任何其他返回值都表示错误。
        if (pthread_mutex_trylock(&_lock) == 0) {
            if (_lru->_totalCost > costLimit) {  
                
                // 获取最新尾节点,在移除尾节点的同时,重新设置_totalCost总byte数据
                _YYLinkedMapNode *node = [_lru removeTailNode]; // 首先这里我已经保证尾结点
                                
                // 如果尾节点存在,添加到Holder数组
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000); //10 ms
        }
    }
    //
    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
            // ?
            [holder count]; // release in queue
        });
    }
}

- (void)_trimToCount:(NSUInteger)countLimit {
    BOOL finish = NO;
    pthread_mutex_lock(&_lock);
    if (countLimit == 0) {
        [_lru removeAll];
        finish = YES;
    } else if (_lru->_totalCount <= countLimit) {
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
            if (_lru->_totalCount > countLimit) {
                _YYLinkedMapNode *node = [_lru removeTailNode]; 
                
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000); //10 ms
        }
    }
    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
            [holder count]; // release in queue
        });
    }
}

- (void)_trimToAge:(NSTimeInterval)ageLimit {
    BOOL finish = NO;
    NSTimeInterval now = CACurrentMediaTime();
    pthread_mutex_lock(&_lock);
    if (ageLimit <= 0) {
        [_lru removeAll];
        finish = YES;
    } else if (!_lru->_tail || (now - _lru->_tail->_time) <= ageLimit)
    {
        //如果_lru->_tail不为空,也就是说如果有数据;设置缓存中对象的最大过期时间(这里作者默认设置为一周) 也就是当前的时间 - 尾结点的时间(尾结点也就是最晚使用的结点) 如果小于一周,缓存对象不需要修剪。
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
            // 当前的时间 - 尾结点的时间 如果大于一周,缓存对象需要修剪。
            if (_lru->_tail && (now - _lru->_tail->_time) > ageLimit) {
                _YYLinkedMapNode *node = [_lru removeTailNode];
                // 添加那些最近从最近最多使用链表中淘汰的页面信息 (Ghost list for LRU)
                [_lruGhost insertNodeAtHead:node];
                
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            /** usleep使用
             作者回答:为了尽量保证所有对外的访问方法都不至于阻塞,这个对象移除的方法应当尽量避免与其他访问线程产生冲突。当然这只能在很少一部分使用场景下才可能有些作用吧,而且作用可能也不明显。。。
             */
            usleep(10 * 1000); //10 ms
        }
    }
    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
            [holder count]; // release in queue
        });
    }
}

- (void)_appDidReceiveMemoryWarningNotification {
    if (self.didReceiveMemoryWarningBlock) {
        self.didReceiveMemoryWarningBlock(self);
    }
    if (self.shouldRemoveAllObjectsOnMemoryWarning) {
        [self removeAllObjects];
    }
}

- (void)_appDidEnterBackgroundNotification {
    if (self.didEnterBackgroundBlock) {
        self.didEnterBackgroundBlock(self);
    }
    if (self.shouldRemoveAllObjectsWhenEnteringBackground) {
        [self removeAllObjects];
    }
}

#pragma mark - public

- (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;
    
    [[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;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
    [_lru removeAll];
    pthread_mutex_destroy(&_lock);
}

- (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;
}

- (void)setReleaseAsynchronously:(BOOL)releaseAsynchronously {
    pthread_mutex_lock(&_lock);
    _lru->_releaseAsynchronously = releaseAsynchronously;
    pthread_mutex_unlock(&_lock);
}

- (BOOL)containsObjectForKey:(id)key {
    if (!key) return NO;
    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);
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (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];
}

// 设置新结点放置在head,key是图片的名称
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
    if (!key) return;
    if (!object) {
        // 如果针对图片的URL(Key)设置image(object)为nil,则移除缓存(隐藏条件)
        [self removeObjectForKey:key];
        return;
    }
    pthread_mutex_lock(&_lock);
 
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    
    
    //
    /** 设置对象关注点
     热端-----------------冷端
     与标准的 LRU 一样,添加数据插入到热端的头
     
     添加过程中,判断如果缓存中对象开销大于用户设置对象开销,修剪(淘汰旧对象),具体修剪过程可以看trimToCost方法描述
     接着继续判断缓存中对象总数 > 用户设置对象总数,修剪(淘汰旧对象)
     */
    
    // 返回当前的绝对时间,在几秒钟内
    NSTimeInterval now = CACurrentMediaTime();
    if (node) { // 如果缓存池中存在该结点,并且清空原开销,重新设置time(当前设置时间)为,因为缓存池对象作者默认设置为一周,如果该对象重新使用,需要重新设置当时使用时间;接着重新设置cost;
        // _cost:位图图像的字节数,重新设置cost,把该节点置于Head位置上
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        [_lru bringNodeToHead:node];
    
    } else {
         // 初始化结点
         node = [_YYLinkedMapNode new];
        node->_cost = cost;
        node->_time = now;
        node->_key = key;
        node->_value = object;
         [_lru insertNodeAtHead:node];
        
    }
    if (_lru->_totalCost > _costLimit) {
        dispatch_async(_queue, ^{
            [self trimToCost:_costLimit];
        });
    }
    if (_lru->_totalCount > _countLimit) {
        _YYLinkedMapNode *node = [_lru removeTailNode];
        if (_lru->_releaseAsynchronously) {
            //  主队列或者新建一个低优先级的NSQualityOfServiceUtility        —-   DISPATCH_QUEUE_PRIORITY_LOW  队列执行
            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]; 这种在queue上调用对象的方法,能保证node是在这个queue上release掉,这个确定么?   
                 作者回答:你可以给 node 加个 dealloc 下断点看看。
                 群众回答:应该是node在执行完这个方法后就出了作用域了,reference会减1,但是此时node不会被dealloc,因为block 中retain了node,使得node的reference count为1,当执完block后,node的reference count又-1,此时node就会在block对应的queue上release了。的确很巧妙
                 */
                [node class]; //hold and release in queue
            });
        }
    }
    pthread_mutex_unlock(&_lock);
}

- (void)removeObjectForKey:(id)key {
    if (!key) return;
    pthread_mutex_lock(&_lock);
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        [_lruGhost insertNodeAtHead: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);
}

- (void)trimToCount:(NSUInteger)count {
    if (count == 0) {
        [self removeAllObjects];
        return;
    }
    [self _trimToCount:count];
}

- (void)trimToCost:(NSUInteger)cost {
    [self _trimToCost:cost];
}

- (void)trimToAge:(NSTimeInterval)age {
    [self _trimToAge:age];
}

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

@end

知识点

  • _trimRecursively方法来监听LRU链表,这是一个递归方法,并且把要修剪的LRU链表的设置到异步线程,如果LRU链表对象数量及周期、开销的超出了设置的初值,使用主队列或全局队列(低优先级)的执行方式来对LRU链表移除操作,这个方法设计的很巧妙,将任务放入到后台执行,并且是递归方法,在调试过程中,当添加对象超出设置的初值时,就会调用该方法,把LRU链表设置结点设置到“零界点“(初始值的范围内),我仔细看了下代码,如果出现内存警告或进入后台就会调用_appDidReceiveMemoryWarningNotification和_appDidEnterBackgroundNotification方法,当这两个方法调用时LRU链表数据全部移除,而_trimRecursively方法是动态的移除结点(近期最少使用的)直至初值范围内,当然还有一个作用是可以动态的监听过期的对象(默认设置为1周),当设置shouldRemoveAllObjectsOnMemoryWarning = NO
    和shouldRemoveAllObjectsWhenEnteringBackground = NO ,这个方法缓存操作就发挥巨大作用。

  • 使用pthread_mutex 来保证结点操作安全,pthread_mutex_trylock方法:非阻塞的互斥锁,函数成功返回0。任何其他返回值都表示错误。

  • 为什么有这么多锁来保证线程安全,这里偏偏用pthread_mutex(原先用的是OSSpinLock)?
    根据作者博文介绍不再安全的 OSSpinLock

图片来自作者博客,地址在上头

可以看到除了 OSSpinLock 外,dispatch_semaphore 和 pthread_mutex 性能是最高的。有消息称,苹果在新系统中已经优化了 pthread_mutex 的性能


[node class]; 这种在queue上调用对象的方法,能保证node是在这个queue上release掉,这个确定么?
作者回答:你可以给 node 加个 dealloc 下断点看看。
群众回答:应该是node在执行完这个方法后就出了作用域了,reference会减1,但是此时node不会被dealloc,因为block 中retain了node,使得node的reference count为1,当执完block后,node的reference count又-1,此时node就会在block对应的queue上release了,的确很巧妙

** usleep使用**
作者回答:为了尽量保证所有对外的访问方法都不至于阻塞,这个对象移除的方法应当尽量避免与其他访问线程产生冲突。当然这只能在很少一部分使用场景下才可能有些作用吧,而且作用可能也不明显。。。

最后知识点总结

  • 作者在YYModel、YYKVStorage、YYMemoryCache 文件中都使用到CFMutableDictionaryRef作为数据存储(可以认为是一个缓存池),效率相对于Foundation高,这里有必要介绍CFMutableDictionary相关API
  • CFMutableDictionaryRef : Core Foundation框架字典,Core Foundation框架 是C语言开发的一套接口,为应用程序提供基本的数据管理和服务功能,它和Foundation框架为相同的功能提供接口,Foundation框架提供的是Objective-C的接口,Core Foundation与Foundation类型转换,需要桥接对象。
  • 初始化一个CFMutableDictionaryRef的实例,

     _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    
  • 删除指定Key数据

    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
    
  • 添加数据:

     void CFDictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value);
     void CFDictionaryAddValue(CFMutableDictionaryRef theDict, const void *key, const void *value);
    

** 添加: **

void CFDictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value);

** explain:**

@param key The key of the value to set into the dictionary. If a key
which matches this key is already present in the dictionary, only
the value is changed ("add if absent, replace if present"). If
no key matches the given key, the key-value pair is added to the
dictionary. If added, the key is retained by the dictionary,
using the retain callback provided

如果参数Key已经存在于字典,替换旧值,如果参数Key不存在于字典,以键值对形式添加至字典

** explain:**

void CFDictionaryAddValue(CFMutableDictionaryRef theDict, const void *key, const void *value);

如果参数Key已经存在于字典,不做任何操作,如果参数Key不存在于字典,以键值对形式添加至字典

** 遍历字典函数: **

void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction applier, void *context);

explain

在字典中调用每个键值对的函数。
如果遍历的是一个可变的集合,它可以保证改变集合的内容安全,使用CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法来遍历容器类可以带来不少性能提升

最后

读了代码还有之前了解一点Runtime知识,objc_cache方法链表作用于方法调用的性能优化(存储近期使用的方法缓存结构),其中包含最近使用的方法指针,以及UITableView的重用机制,他们共同的特征是把使用的过方法或cell存入缓存,再次使用时从缓存取出,于是又结合了看了ARC算法,结合YYMemoryCache源码实践一番(可选LRU/类似ARC/MRU算法),也许你看了源码ARC算法不是有4个链表吗,如果再次添加数据时已存在数据时,结点前置(LRU位置),所以我就用了2个链表来操作数据,LRUGhost链表来存储移除的数据,再次添加时如果LRU没有相关的数据时再从LRUGhost链表中查找,以及LRUGhost链表出现内存警告时也做了一些处理,效率简单测试了一下,还是LRU的好,可能是我过多判断和操作了,有空的朋友可以测试测试,项目源代码贴在下面

LTMemoryCache.h

typedef NS_OPTIONS(NSUInteger, LTCacheListType) {
    LTCacheListTypeLRU = 0,
    LTCacheListTypeMRU = 1 << 0,
    LTCacheListTypeLRUGhost = 1 << 1,
};

LTMemoryCache.m

//
//  LTMemoryCache.m
//  LTMemoryCache
//
//  Created by lmj  on 16/6/12.
//  Copyright © 2016年 linmingjun. All rights reserved.
//

#import "LTMemoryCache.h"
#import <UIKit/UIKit.h>
#import <CoreFoundation/CoreFoundation.h>
#import <QuartzCore/QuartzCore.h>
#import <pthread.h>

static inline dispatch_queue_t LTMemoryCacheGetReleaseQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}

@interface _LTLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _LTLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _LTLinkedMapNode *_next; // retained by dic
    id _key;
    id _value;
    NSUInteger _cost;
    NSTimeInterval _time;
}
@end

@implementation _LTLinkedMapNode
@end

@interface _LTLinkedMap : NSObject {
    
    @package
    CFMutableDictionaryRef _dic;
    LTCacheListType _type;
    NSUInteger _totalCost;
    NSUInteger _totalCount;
    _LTLinkedMapNode *_head;
    _LTLinkedMapNode *_tail;
    BOOL _releaseOnMainThread;
    BOOL _releaseAsynchronously;
}


- (void)insertNodeAtHead:(_LTLinkedMapNode *)node;


- (void)bringNodeToHead:(_LTLinkedMapNode *)node;

- (void)removeNode:(_LTLinkedMapNode *)node;

- (_LTLinkedMapNode *)removeTailNode;

- (void)removeAll;

@end

@implementation _LTLinkedMap

- (instancetype)init {
    self = [super init];
    _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    _releaseOnMainThread = NO;
    _releaseAsynchronously = YES;
    return self;
}




- (void)dealloc {
    CFRelease(_dic);
}

- (void)insertNodeAtHead:(_LTLinkedMapNode *)node {
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    _totalCost += node->_cost;
    _totalCount++;
    
    if (_head) {
        // 把插入的数据作为头结点
        node->_next = _head;
        // 设置第二个节点(原先头结点的)的前继
        _head->_prev = node;
        // 设置头节点为插入的节点
        _head = node;
    } else { // 如果插入的是第一个数据
        _head = _tail = node;
    }
}

// node移到head(头结点) 每次缓存命中时将页面转移LRU位置(head)
- (void)bringNodeToHead:(_LTLinkedMapNode *)node {
    // 如果该结点是头结点,直接返回
    if (_head == node) return;
    if (_tail == node) {
        // 因为最后一个节点要往(head)头节点移动所以尾节点要指向前一个节点
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        /**  如果该结点位于链表之间
         */
        // 当前结点的前驱线索指向当前结点的后继
        node->_next->_prev = node->_prev;
        // 当前结点的后继线索指向前驱
        node->_prev->_next = node->_next;
    }
    // 当前结点的后继线索指向头结点,那么head为第二结点
    node->_next = _head;
    // 非循环双向链表,将前继线索置为nil
    node->_prev = nil;
    // 此时的head作为第二个结点,第二个结点前驱线索指向第一个节点
    _head->_prev = node;
    // 头结点指针指向第一个节点
    _head = node;
    /** 如果是双向循环链表:取消 node->_prev = nil; 这行代码
     head->_prev->_next = node;
     node->_next = head;
     */
}

/// 移除内节点和更新的总成本。节点在已经DIC内。
- (void)removeNode:(_LTLinkedMapNode *)node {
    // 从缓冲池中移除指定对象
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    _totalCost -= node->_cost;
    _totalCount--;
    // 移除节点位于头尾之间
    if (node->_next)
        node->_next->_prev = node->_prev;
    if (node->_prev)
        node->_prev->_next = node->_next;

    // 移除节点是头节点,头结点指向下一个结点
    if (_head == node)
        _head = node->_next;
    // 移除节点是尾节点,尾结点指向前一个结点
    if (_tail == node)
        _tail = node->_prev;
}


- (_LTLinkedMapNode *)removeTailNode {
    if (!_tail) return nil;
    _LTLinkedMapNode *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;
}

- (_LTLinkedMapNode *)removeHeadNode {
    if (!_head) return nil;
    _LTLinkedMapNode *head = _head; // 记录头结点
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_head->_key));
    // 更新的总成本和数据量
    _totalCost -= _head->_cost;
    _totalCount--;
    if (_head == _tail) {
        _head = _tail = nil;
    } else {
        
        // 设置头节点指向后一个节点
        _head = _head->_next;
        // 设置头节点的前驱线索置为nil。
        _head->_prev = nil;
    }
    // 返回头节点
    return head;
}


- (void)removeAll {
    _totalCost = 0;
    _totalCount = 0;
    _head = nil;
    _tail = nil;
    if (CFDictionaryGetCount(_dic) > 0) {
        CFMutableDictionaryRef holder = _dic;
        // 初始化字典
        _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        
        if (_releaseAsynchronously) {
            dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
            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);
        }
    }
}

@end



@implementation LTMemoryCache {
    pthread_mutex_t _lock;
    _LTLinkedMap *_lru;
    _LTLinkedMap *_lruGhost;
    dispatch_queue_t _queue;
}

- (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];
    });
}

- (void)_trimInBackground {
    dispatch_async(_queue, ^{
        [self _trimToCost:self->_costLimit];
        [self _trimToCount:self->_countLimit];
        [self _trimToAge:self->_ageLimit];
    });
}

#pragma mark - remove/setObjectTrim

- (void)removeAllList {
    switch (LTCacheListTypeLRU) {
        case LTCacheListTypeLRUGhost :
            [_lru removeAll];
            [_lruGhost removeAll];
            break;
        case LTCacheListTypeMRU :
        case LTCacheListTypeLRU :
            [_lru removeAll];
            break;
        default:
            break;
    }
}

- (_LTLinkedMapNode *)removeHeadOrTail {
    _LTLinkedMapNode *node = [_lru removeTailNode];
    switch (_type) {
        case LTCacheListTypeLRUGhost :
            [_lruGhost insertNodeAtHead:node];
            break;
        case LTCacheListTypeMRU :
            node = [_lru removeHeadNode];
        case LTCacheListTypeLRU :
        default:
            break;
    }
    return node;
}

- (void)setObjectLruOrLruGhost:(_LTLinkedMap *)_LruOrLruGhost {
    if (_LruOrLruGhost->_totalCost > _costLimit) {
        dispatch_async(_queue, ^{
            [self trimToCost:_costLimit];
        });
    }
    if (_LruOrLruGhost->_totalCount > _countLimit) {
        
        _LTLinkedMapNode * node = [_LruOrLruGhost removeTailNode];

        if ([_LruOrLruGhost isEqual:_lru]) {
            [_lruGhost insertNodeAtHead:node];
           
        } else {
            if (_lruGhost->_totalCount == _countLimit) {
               
                [_lruGhost removeAll];
                
            }
        }
        if (_LruOrLruGhost->_releaseAsynchronously) {
            dispatch_queue_t queue = _LruOrLruGhost->_releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                [node class];
            });
        } else if (_LruOrLruGhost->_releaseOnMainThread && !pthread_main_np()) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [node class];
            });
        }
    }
}



- (void)_trimToCost:(NSUInteger)costLimit {
    BOOL finish = NO;
    pthread_mutex_lock(&_lock);
    if (costLimit == 0) {
        
        [self removeAllList];
        
        finish = YES;
    } else if (_lru->_totalCost <= costLimit) {
        
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
            if (_lru->_totalCost > costLimit) {
                _LTLinkedMapNode *node = [self removeHeadOrTail];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000);
        }
    }
    
    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
          
            [holder count];
        });
    }
}

- (void)_trimToCount:(NSUInteger)countLimit {
    BOOL finish = NO;
    pthread_mutex_lock(&_lock);
    if (countLimit == 0) {
        [self removeAllList];
        finish = YES;
    } else if (_lru->_totalCount  <= countLimit) {
        finish = YES;
    }
    
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
            if (_lru->_totalCount > countLimit) {
                _LTLinkedMapNode *node = [self removeHeadOrTail];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000); //10 ms
        }
    }

    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
            [holder count]; // release in queue
        });
    }
}

- (void)_trimToAge:(NSTimeInterval)ageLimit {
    BOOL finish = NO;
    NSTimeInterval now = CACurrentMediaTime();
    pthread_mutex_lock(&_lock);
    if (ageLimit <= 0) {
        [self removeAllList];
        finish = YES;
    } else if (!_lru->_tail || (now - _lru->_tail->_time) <= ageLimit)
    {
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    NSMutableArray *holder = [NSMutableArray new];
    while (!finish) {
        if (pthread_mutex_trylock(&_lock) == 0) {
           
            if (_lru->_tail && (now - _lru->_tail->_time) > ageLimit) {
                _LTLinkedMapNode *node = [self removeHeadOrTail];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            usleep(10 * 1000);
        }
    }
    if (holder.count) {
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
        dispatch_async(queue, ^{
            [holder count];
        });
    }
}

- (void)_appDidReceiveMemoryWarningNotification {
    if (self.didReceiveMemoryWarningBlock) {
        self.didReceiveMemoryWarningBlock(self);
    }
    if (self.shouldRemoveAllObjectsOnMemoryWarning) {
        [self removeAllObjects];
    }
}

- (void)_appDidEnterBackgroundNotification {
    if (self.didEnterBackgroundBlock) {
        self.didEnterBackgroundBlock(self);
    }
    if (self.shouldRemoveAllObjectsWhenEnteringBackground) {
        [self removeAllObjects];
    }
}

#pragma mark - Init Method

- (instancetype)init {

    return [self initWithCache:LTCacheListTypeLRU];
}

- (instancetype)initWithCache:(LTCacheListType )type {
    self = [super init];
    
    pthread_mutex_init(&_lock, NULL);
    _lru = [_LTLinkedMap new];
    _lruGhost = [_LTLinkedMap new];
    _queue = dispatch_queue_create("com.cache.memory", DISPATCH_QUEUE_SERIAL);
    
//    _countLimit = NSUIntegerMax;
    _countLimit = NSUIntegerMax;
    _costLimit = NSUIntegerMax;
    _ageLimit = DBL_MAX;
    _autoTrimInterval = 5.0;
    _shouldRemoveAllObjectsOnMemoryWarning = YES;
    _shouldRemoveAllObjectsWhenEnteringBackground = YES;
    _type = type;
    [[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;
}


- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
    [_lru removeAll];
    [_lruGhost removeAll];
    pthread_mutex_destroy(&_lock);
}

- (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;
    _lruGhost->_releaseOnMainThread = releaseOnMainThread;
    pthread_mutex_unlock(&_lock);
}

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

- (void)setReleaseAsynchronously:(BOOL)releaseAsynchronously {
    pthread_mutex_lock(&_lock);
    _lru->_releaseAsynchronously = releaseAsynchronously;
    _lruGhost->_releaseAsynchronously = releaseAsynchronously;
    pthread_mutex_unlock(&_lock);
}

- (BOOL)containsObjectForKey:(id)key {
    if (!key) return NO;
    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);
    _LTLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        node->_time = CACurrentMediaTime();
        [_lru bringNodeToHead:node];
    } else {
        node = CFDictionaryGetValue(_lruGhost->_dic, (__bridge const void *)(key));
        switch (_type) {
            case LTCacheListTypeLRUGhost :
                if (node) {
                    node->_time = CACurrentMediaTime();
                     [_lruGhost removeNode:node];
                    [_lru insertNodeAtHead:node];
                   
                }
                break;
            case LTCacheListTypeMRU :
            case LTCacheListTypeLRU :

                break;
            default:
                break;
        }
    }
    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;
    if (!object) {
        [self removeObjectForKey:key];
        return;
    }
    pthread_mutex_lock(&_lock);
    _LTLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    NSTimeInterval now = CACurrentMediaTime();
    if (node) {
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        [_lru bringNodeToHead:node];
    } else {
        node = CFDictionaryGetValue(_lruGhost->_dic, (__bridge const void *)(key));
        switch (_type) {
            case LTCacheListTypeLRUGhost :
                if (node) {
                    node->_time = CACurrentMediaTime();
                    [_lruGhost removeNode:node];
                    [_lru insertNodeAtHead:node];
                    
                } else {
                    node = [_LTLinkedMapNode new];
                    node->_cost = cost;
                    node->_time = now;
                    node->_key = key;
                    node->_value = object;
                    [_lru insertNodeAtHead:node];
                }
                break;
            case LTCacheListTypeMRU :
            case LTCacheListTypeLRU :
                node = [_LTLinkedMapNode new];
                node->_cost = cost;
                node->_time = now;
                node->_key = key;
                node->_value = object;
                [_lru insertNodeAtHead:node];
                break;
            default:
            break;
        }
    }
    switch (_type) {
        case LTCacheListTypeLRUGhost :
            [self setObjectLruOrLruGhost:_lru];
//            [self setObjectLruOrLruGhost:_lruGhost];
            break;
        case LTCacheListTypeMRU :
        case LTCacheListTypeLRU :
            [self setObjectLruOrLruGhost:_lru];
        default:
            break;
    }
    pthread_mutex_unlock(&_lock);
}

- (void)removeObjectForKey:(id)key {
    if (!key) return;
    pthread_mutex_lock(&_lock);
    _LTLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        
        switch (_type) {
            case LTCacheListTypeLRUGhost:
                [_lru removeNode:node];
                [_lruGhost insertNodeAtHead:node];
               
                break;
                case LTCacheListTypeMRU:
                case LTCacheListTypeLRU:
                 [_lru removeNode:node];
                break;
            default:
                break;
        }
        
        
        if (_lru->_releaseAsynchronously) {
            dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : LTMemoryCacheGetReleaseQueue();
            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];
    [_lruGhost removeAll];
    pthread_mutex_unlock(&_lock);
}

- (void)trimToCount:(NSUInteger)count {
    if (count == 0) {
        [self removeAllObjects];
        return;
    }
    [self _trimToCount:count];
}

- (void)trimToCost:(NSUInteger)cost {
    [self _trimToCost:cost];
}

- (void)trimToAge:(NSTimeInterval)age {
    [self _trimToAge:age];
}

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

@end

源代码文件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容