美文网首页内存
iOS 实现内存缓存

iOS 实现内存缓存

作者: Sweet丶 | 来源:发表于2020-09-17 17:42 被阅读0次

    计算机为了提升访问数据的速度,实现了CPU的多级缓存机制,cpu计算时经常要用到的数据会被从其他地方读到cache里,但cache的大小是有限的,在缓存空间不够用时,就势必要逐出数据为新数据腾出空间... 由此引入了LRU策略.

    一、实现内存缓存的思路

    LRU(Least Recently use), 在空间不够时,将最近最少使用的数据删除来为新数据腾空间,为了实现这样的功能,需要思考的东西🤔:

    1. 一份一份的数据需要按照时间排列,内存不足时从最老的一份开始删除,直到满足条件。这里令最新数据放在最前面,最老的数据放到最后面。
    2. 新数据读到内存时,需要插入到最前面;老数据再次被访问时,移动到最前面。
    3. 访问数据效率要快, 复杂度为O(1).
    4. 数据的写入、更改需要加锁。

    综上所述,应该使用双向链表来实现这些数据的排列,使得在移动数据到头部时,时间复杂度O(1)。如果是单向链表,则在移动节点时,该节点的上一节点的next指向下一节点时, 上一节点不好访问。
    同时为了使得在访问数据时复杂度O(1), 我们还需要使用哈希表我们在iOS中,使用字典NSDictionary和NSSet的底层实现原理

    二、具体实现代码

    YYCache框架中的YYMemoryCache类就是根据LRU这个策略来实现的,具体代码可以参考之。
    关键在于双向链表的节点设置双向链表的设计缓存类的实现 三部分对应了三个类_YYLinkedMapNode 、_YYLinkedMap、YYMemoryCache.

    // 一、双向链表的节点设置
    @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
    
    
    // 二、双向链表的设计
    @implementation _YYLinkedMap
    - (void)insertNodeAtHead:(_YYLinkedMapNode *)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;
        }
    }
    
    - (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;
    }
    - (_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;
    }
    @end
    
    // 三、缓存类的实现
    @implementation YYMemoryCache
    - (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 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) {
            _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) {
                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);
    }
    @end
    

    在iOS中系统提供了NSCache这个类来实现内存缓存,为什么选择YYCaChe?

    • It uses LRU (least-recently-used) to remove objects; NSCache's eviction method is non-deterministic.
    • It can be controlled by cost, count and age; NSCache's limits are imprecise.
    • It can be configured to automatically evict objects when receive memory
      warning or app enter background.

    相关文章

      网友评论

        本文标题:iOS 实现内存缓存

        本文链接:https://www.haomeiwen.com/subject/sdtryktx.html