IOS缓存管理之YYCache使用详解

前言:

最近一直在致力于为公司app添加缓存功能,为了寻找一个最佳方案,这几天先做个技术预研,经过这两天的查找资料基本上确定了两个开源框架进行选择,这两个开源框架分别是:PINCache、YYCache,上篇已经简单介绍了PINCache使用,本篇主要来学习一下YYCache的使用方式,以及和PINCache性能的简单对比。

关于YYCache

1. 内存缓存(YYMemoryCache)

存储的单元是_YYLinkedMapNode,除了key和value外,还存储了它的前后Node的地址_prev,_next.整个实现基于_YYLinkedMap,它是一个双向链表,除了存储了字典_dic外,还存储了头结点和尾节点.它实现的功能很简单,就是:有新数据了插入链表头部,访问过的数据结点移到头部,内存紧张时把尾部的结点移除.就这样实现了淘汰算法.因为内存访问速度很快,锁占用的时间少,所以用的速度最快的OSSpinLockLock

2. 硬盘缓存(YYDiskCache)

采用的是文件和数据库相互配合的方式.有一个参数inlineThreshold,默认20KB,小于它存数据库,大于它存文件.能获得效率的提高.key:path,value:cache存储在NSMapTable里.根据path获得cache,进行一系列的set,get,remove操作更底层的是YYKVStorage,它能直接对sqlite和文件系统进行读写.每次内存超过限制时,select key, filename, size from manifest order by last_access_time desc limit ?1会根据时间排序来删除最近不常用的数据.硬盘访问的时间比较长,如果用OSSpinLockLock锁会造成CPU消耗过大,所以用的dispatch_semaphore_wait来做.

YYCache使用

1.同步方式

  //模拟数据
  NSString *value=@"I want to know who is lcj ?";
  //模拟一个key
  //同步方式
  NSString *key=@"key";
  YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
  //根据key写入缓存value
  [yyCache setObject:value forKey:key];
  //判断缓存是否存在
  BOOL isContains=[yyCache containsObjectForKey:key];
  NSLog(@"containsObject : %@", isContains?@"YES":@"NO");
  //根据key读取数据
  id vuale=[yyCache objectForKey:key];
  NSLog(@"value : %@",vuale);
  //根据key移除缓存
  [yyCache removeObjectForKey:key];
  //移除所有缓存
  [yyCache removeAllObjects];

2.异步方式

  //模拟数据
  NSString *value=@"I want to know who is lcj ?";
  //模拟一个key
  //异步方式
  NSString *key=@"key";
  YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
  //根据key写入缓存value
  [yyCache setObject:value forKey:key withBlock:^{
    NSLog(@"setObject sucess");
  }];
  //判断缓存是否存在
  [yyCache containsObjectForKey:key withBlock:^(NSString * _Nonnull key, BOOL contains) {
    NSLog(@"containsObject : %@", contains?@"YES":@"NO");
  }];

  //根据key读取数据
  [yyCache objectForKey:key withBlock:^(NSString * _Nonnull key, id<NSCoding> _Nonnull object) {
    NSLog(@"objectForKey : %@",object);
  }];

  //根据key移除缓存
  [yyCache removeObjectForKey:key withBlock:^(NSString * _Nonnull key) {
    NSLog(@"removeObjectForKey %@",key);
  }];
  //移除所有缓存
  [yyCache removeAllObjectsWithBlock:^{
    NSLog(@"removeAllObjects sucess");
  }];

  //移除所有缓存带进度
  [yyCache removeAllObjectsWithProgressBlock:^(int removedCount, int totalCount) {
    NSLog(@"removeAllObjects removedCount :%d totalCount : %d",removedCount,totalCount);
  } endBlock:^(BOOL error) {
    if(!error){
      NSLog(@"removeAllObjects sucess");
    }else{
      NSLog(@"removeAllObjects error");
    }
  }];

YYCache缓存LRU清理

LRU(Least Recently Used)算法大家都比较熟悉,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉。接下来我们测试一下

  YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
  [yyCache.memoryCache setCountLimit:50];//内存最大缓存数据个数
  [yyCache.memoryCache setCostLimit:1*1024];//内存最大缓存开销 目前这个毫无用处
  [yyCache.diskCache setCostLimit:10*1024];//磁盘最大缓存开销
  [yyCache.diskCache setCountLimit:50];//磁盘最大缓存数据个数
  [yyCache.diskCache setAutoTrimInterval:60];//设置磁盘lru动态清理频率 默认 60秒

模拟一下清理

  for(int i=0 ;i<100;i++){
    //模拟数据
    NSString *value=@"I want to know who is lcj ?";
    //模拟一个key
    NSString *key=[NSString stringWithFormat:@"key%d",i];
    [yyCache setObject:value forKey:key];
  }

  NSLog(@"yyCache.memoryCache.totalCost:%lu",(unsigned long)yyCache.memoryCache.totalCost);
  NSLog(@"yyCache.memoryCache.costLimit:%lu",(unsigned long)yyCache.memoryCache.costLimit);

  NSLog(@"yyCache.memoryCache.totalCount:%lu",(unsigned long)yyCache.memoryCache.totalCount);
  NSLog(@"yyCache.memoryCache.countLimit:%lu",(unsigned long)yyCache.memoryCache.countLimit);

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(120 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    NSLog(@"yyCache.diskCache.totalCost:%lu",(unsigned long)yyCache.diskCache.totalCost);
    NSLog(@"yyCache.diskCache.costLimit:%lu",(unsigned long)yyCache.diskCache.costLimit);

    NSLog(@"yyCache.diskCache.totalCount:%lu",(unsigned long)yyCache.diskCache.totalCount);
    NSLog(@"yyCache.diskCache.countLimit:%lu",(unsigned long)yyCache.diskCache.countLimit);

    for(int i=0 ;i<100;i++){
      //模拟一个key
      NSString *key=[NSString stringWithFormat:@"whoislcj%d",i];
      id vuale=[yyCache objectForKey:key];
      NSLog(@"key :%@ value : %@",key ,vuale);
    }

  });

YYCache和PINCache一样并没有实现基于最大内存开销进行LRU,不过YYCache实现了最大缓存数据个数进行LRU清理,这一点也是选择YYCache原因之一,对于YYCache磁盘LRU清理并不是及时清理,而是后台开启一个定时任务进行RLU清理操作,定时时间默认是60s。

YYCache与PINCache对比

对于我这里的使用场景大部分用于缓存json字符串,我这里就以存储字符串来对比一下写入与读取效率

1.写入性能对比

YYCache

  //模拟数据
  NSString *value=@"I want to know who is lcj ?";
  //模拟一个key
  NSString *key=@"key";
  //YYCache
  YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
  //写入数据
  CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
  [yyCache setObject:value forKey:key withBlock:^{
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();

    NSLog(@" yyCache async setObject time cost: %0.5f", end - start);
  }];

  CFAbsoluteTime start1 = CFAbsoluteTimeGetCurrent();
  [yyCache setObject:value forKey:key];
  CFAbsoluteTime end1 = CFAbsoluteTimeGetCurrent();
  NSLog(@" yyCache sync setObject time cost: %0.5f", end1 - start1);

运行结果

PINCache

   //PINCache
  //模拟数据
  NSString *value=@"I want to know who is lcj ?";
  //模拟一个key
  NSString *key=@"key";
  PINCache *pinCache=[PINCache sharedCache];
  //写入数据
  CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
  [pinCache setObject:value forKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) {
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();

    NSLog(@" pincache async setObject time cost: %0.5f", end - start);
  }];

  CFAbsoluteTime start1 = CFAbsoluteTimeGetCurrent();
  [pinCache setObject:value forKey:key];
  CFAbsoluteTime end1 = CFAbsoluteTimeGetCurrent();
  NSLog(@" pinCache sync setObject time cost: %0.5f", end1 - start1);

运行结果

通过上面的测试可以看出 同样大小的数据,无论同步方式还是异步方式,YYCache性能都要由于PINCache。

2.读取性能对比

YYCache

  YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
  //模拟一个key
  NSString *key=@"key";
  CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
  //读取数据
  [yyCache objectForKey:key withBlock:^(NSString * _Nonnull key, id<NSCoding> _Nonnull object) {
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@" yyCache async objectForKey time cost: %0.5f", end - start);
  }];

  CFAbsoluteTime start1 = CFAbsoluteTimeGetCurrent();
  [yyCache objectForKey:key];
  CFAbsoluteTime end1 = CFAbsoluteTimeGetCurrent();
  NSLog(@" yyCache sync objectForKey time cost: %0.5f", end1 - start1);

运行结果:

PINCache

 PINCache *pinCache=[PINCache sharedCache];
  //模拟一个key
  NSString *key=@"key";
  CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
  //读取数据
  [pinCache objectForKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) {
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@" pincache async objectForKey time cost: %0.5f", end - start);
  }] ;

  CFAbsoluteTime start1 = CFAbsoluteTimeGetCurrent();
  [pinCache objectForKey:key];
  CFAbsoluteTime end1 = CFAbsoluteTimeGetCurrent();
  NSLog(@" pinCache objectForKey time cost: %0.5f", end1 - start1);

运行结果:

通过运行结果,在读取方面YYCache也是优于PINCache。

总结:

经过一番查阅资料和自己写例子测试,最终项目中决定使用YYCache进行缓存管理。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • IOS缓存管理之YYCache使用详解

    前言: 最近一直在致力于为公司app添加缓存功能,为了寻找一个最佳方案,这几天先做个技术预研,经过这两天的查找资料基本上确定了两个开源框架进行选择,这两个开源框架分别是:PINCache.YYCache,上篇已经简单介绍了PINCache使用,本篇主要来学习一下YYCache的使用方式,以及和PINCache性能的简单对比. 关于YYCache 1. 内存缓存(YYMemoryCache) 存储的单元是_YYLinkedMapNode,除了key和value外,还存储了它的前后Node的地址_p

  • IOS 创建并发线程的实例详解

    IOS 创建并发线程的实例详解 创建并发线程 主线程一般都是处理UI界面及用户交互的事儿的.其他的事一般就要另外的线程去处理,如下载,计算等... 现在先简单创建3个线程,分别打印出1-1000,,为了方便,线程3就放在主线程中执行. - (void) firstCounter{ @autoreleasepool { NSUInteger counter = 0; for (counter = 0; counter < 1000; counter++){ NSLog(@"First Cou

  • iOS NSURLProtocol的具体使用方法详解

    本文介绍了iOS NSURLProtocol的具体使用方法详解,分享给大家,具体如下: NSURLProtocol定义 这两天在优化项目,无意间看到了NSURLProtocol,学习一下顺便总结下来. NSURLProtocol也是苹果众多黑魔法中的一种,能够让你去重新定义苹果的URL加载系统 (URL Loading System)的行为,URL Loading System里有许多类用于处理URL请求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSes

  • 使用jquery-easyui的布局layout写后台管理页面的代码详解

    先在官网下载easyui文档 引入头部文件 <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/jquery-easyui-1.3.3/themes/default/easyui.css" rel="external nofollow" rel="external nofollow"

  • Python3标准库之functools管理函数的工具详解

    1. functools管理函数的工具 functools模块提供了一些工具来调整或扩展函数和其他callable对象,从而不必完全重写. 1.1 修饰符 functools模块提供的主要工具就是partial类,可以用来"包装"一个有默认参数的callable对象.得到的对象本身就是callable,可以把它看作是原来的函数.它与原函数的参数完全相同,调用时还可以提供额外的位置或命名函数.可以使用partial而不是lambda为函数提供默认参数,有些参数可以不指定. 1.1.1 部

  • Python 分布式缓存之Reids数据类型操作详解

    1.Redis API 1.安装redis模块 $ pip3.8 install redis 2.使用redis模块 import redis # 连接redis的ip地址/主机名,port,password=None r = redis.Redis(host="127.0.0.1",port=6379,password="gs123456") 3.redis连接池 redis-py使用connection pool来管理对一个redis server的所有连接,避

  • IOS之WebSocket框架Starscream案例详解

    传统的网络技术 (也就是 Berkeley sockets) 被认为是可靠和稳定的.但是 Berkeley socket 在某些 web 技术,比如代理和防火墙下不太好使.WebSocket 出现于 2011 年,是一种在客户端和服务端之间建立双向通讯的新技术.WebSocket 比起多个 HTTP 请求来说更有效率并允许长连接. 在 iOS 上使用 WebSocket 并不是那么容易.iOS 和 Mac 库 Starscream 的出现,极大地简化了 WebSocket 的创建和使用. 注:本

  • Tomcat用户管理的优化配置详解

    目录 tomcat用户管理配置 tomcat优化 一.tomcat中的三种运行模式之运行模式的优化 二.tomcat执行器(线程池)的优化 三.tomcat优化之禁用AJP连接器实现动静分离 四.tomcat中JVM参数优化 tomcat用户管理配置 在tomcat-users.xml中添加用户: <role rolename="manager"/> <role rolename="manager-gui"/> <role rolena

  • Node.js基础入门之缓存区与文件操作详解

    目录 缓存区 1. 什么是缓存区? 2. 创建指定长度的缓存区 3. 通过数组创建缓存区 4. 通过字符串创建缓存区 5. 读写缓存区 6. 复制缓存区 文件操作 1. 异步直接读取 2. 同步直接读取 3. 流式读取 4. 写入文件 5. 流式写入文件 6. 读取文件信息 7. 删除文件 8. 管道 9. 链式流 经过前面三天的学习,Node.js的基础知识已逐渐掌握,今天继续学习缓存区和文件操作,并稍加整理加以分享,如有不足之处,还请指正. 缓存区 1. 什么是缓存区? JavaScript

  • SpringBoot集成本地缓存性能之王Caffeine示例详解

    目录 引言 Spring Cache 是什么 集成 Caffeine 核心原理 引言 使用缓存的目的就是提高性能,今天码哥带大家实践运用 spring-boot-starter-cache 抽象的缓存组件去集成本地缓存性能之王 Caffeine. 大家需要注意的是:in-memeory 缓存只适合在单体应用,不适合与分布式环境. 分布式环境的情况下需要将缓存修改同步到每个节点,需要一个同步机制保证每个节点缓存数据最终一致. Spring Cache 是什么 不使用 Spring Cache 抽象

随机推荐