美文网首页内存
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.

相关文章

  • YYCache 源码学习总结

    YYCache 内存分为内存缓存和磁盘缓存 内存缓存 1.内存缓存实现通过CFMutableDictionaryR...

  • iOS 实现内存缓存

    计算机为了提升访问数据的速度,实现了CPU的多级缓存机制,cpu计算时经常要用到的数据会被从其他地方读到cache...

  • 内存缓存那些事

    内存缓存 缓存分为2类,内存缓存和磁盘缓存,今天说的是内存缓存。 内存缓存实现很多种方式,最简单的是就是用NSMu...

  • SDWebImage的实现原理

    SDWebImage底层实现有沙盒缓存机制,主要由三块组成 内存图片缓存内存操作缓存磁盘沙盒缓存 内部实现过程: ...

  • Universal-Image-Loader(2)

    10.MemoryCache MemoryCache是实现内存缓存的类,不管是内存缓存还是磁盘缓存,对于Image...

  • picasso+okhttp3实现图片缓存

    1. 实现内存缓存 picasso 内部默认实现了内存缓存,其大小为内存的1/7,如果想修改这个数据或者想自己调整...

  • SDWebImage底层实现原理及内部实现过程

    SDWebImage底层实现有沙盒缓存机制,主要由三块组成 1、内存图片缓存 2、内存操作缓存 3、磁盘沙盒缓存 ...

  • SDWebImage使用方法及原理

    SDWebImage底层实现有沙盒缓存机制,主要由三块组成 1、内存图片缓存 2、内存操作缓存 3、磁盘沙盒缓存 ...

  • SDWebImage底层

    SDWebImage底层实现有沙盒缓存机制,主要由三块组成 1、内存图片缓存 2、内存操作缓存 3、磁盘沙盒缓存 ...

  • iOS开发--SDWebImage底层实现原理及内部实现过程

    SDWebImage底层实现有沙盒缓存机制,主要由三块组成 1、内存图片缓存 2、内存操作缓存 3、磁盘沙盒缓存 ...

网友评论

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

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