YYMemoryCache分析

2022-02-27  本文已影响0人  honzon_0

YYMemoryCache主要分析

缓存的核心原则是所有操作快速,所以应该尽量避免所有的遍历数据操作

双向链表
@interface _YYLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
    id _key;
    id _value;
    ...
}
@end
哈希表与虚拟头节点
@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic; // do not set object directly
    ...
    _YYLinkedMapNode *_head; // MRU, do not change it directly
    _YYLinkedMapNode *_tail; // LRU, do not change it directly
    ...
}
LRU算法实现

新增节点添加到链表首位

- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    //...
    if (_head) {// 链表不为空
        //新增节点变为新的首节点
        node->_next = _head;
        _head->_prev = node;
        _head = node;
    } else {//链表为空
        _head = _tail = node;
    }
}

移动节点到首位

- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    //节点就在首位
    if (_head == node) return;
    
    //如果是尾节点
    if (_tail == node) {
        //尾节点更新
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        //节点前驱更新
        node->_next->_prev = node->_prev;
        //节点后继更新
        node->_prev->_next = node->_next;
    }
    
    //节点变更为首节点
    node->_next = _head;
    node->_prev = nil;
    _head->_prev = node;
    _head = node;
}

删除节点

- (void)removeNode:(_YYLinkedMapNode *)node {
    ...
    //更新后继节点的前驱
    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;
}

LRU算法是最近最先使用,那么存在着最远最少使用淘汰,即需要删除尾节点操作

- (_YYLinkedMapNode *)removeTailNode {
    //空链表
    if (!_tail) return nil;
    
    _YYLinkedMapNode *tail = _tail;
    //...
    //一个节点
    if (_head == _tail) {
        _head = _tail = nil;
    } else {//多个节点
        //尾节点更新即可
        _tail = _tail->_prev;
        _tail->_next = nil;
    }
    return tail;
}
线程操作锁

最开始是自旋锁OSSpinLock

OSSpinLock _lock;

_lock = OS_SPINLOCK_INIT;//忙等待锁,线程反复检查锁变量是否可用,不会挂起

OSSpinLockLock(&_lock);
//Code..
OSSpinLockUnlock(&_lock);

后因为自旋锁有优先级反转问题,因此改为互斥锁

互斥锁 - 每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

pthread_mutex_t _lock;

pthread_mutex_init(&_lock, NULL); //互斥锁  

pthread_mutex_lock(&_lock);
//Code
pthread_mutex_unlock(&_lock);

线程优先级反转

共享资源Data
任务线程Task1 优先级Low
任务线程Taks2 优先级High
自旋锁Lock 

1. Task1访问Data  Lock加锁
2. Task2访问Data  等待锁释放,因为忙等,不会挂起并且Task2优先级高,占用大量CPU时间
3. Task1需要的CPU时间不够,任务无法完成,Lock无法解开
4. 形成短暂死锁
cache内部变量内存释放队列

cache内部用到的对象内存在异步线程释放,减少主线程压力

// 一个对象释放常驻队列
static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}

xxx   ...
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
dispatch_async(queue, ^{
    [xxx func]; //利用block将对象捕获,在异步线程释放
});
内存警告时清除缓存
// 系统通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];


//双链表清空
- (void)removeAll {
    ...
    //头节点清空
    _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);//当前线程释放
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读