IOS 缓存管理之 YYCache 使用

di846073 8年前
   <h3>前言:</h3>    <p>最近一直在致力于为公司app添加缓存功能,为了寻找一个最佳方案,这几天先做个技术预研,经过这两天的查找资料基本上确定了两个开源框架进行选择,这两个开源框架分别是:PINCache、YYCache,上篇已经简单介绍了PINCache使用,本篇主要来学习一下YYCache的使用方式,以及和PINCache性能的简单对比。</p>    <h3>关于YYCache</h3>    <p>1. 内存缓存(YYMemoryCache)</p>    <p>存储的单元是_YYLinkedMapNode,除了key和value外,还存储了它的前后Node的地址_prev,_next.整个实现基于_YYLinkedMap,它是一个双向链表,除了存储了字典_dic外,还存储了头结点和尾节点.它实现的功能很简单,就是:有新数据了插入链表头部,访问过的数据结点移到头部,内存紧张时把尾部的结点移除.就这样实现了淘汰算法.因为内存访问速度很快,锁占用的时间少,所以用的速度最快的OSSpinLockLock</p>    <p>2. 硬盘缓存(YYDiskCache)</p>    <p>采用的是文件和数据库相互配合的方式.有一个参数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来做.</p>    <h3>YYCache使用</h3>    <p>1.同步方式</p>    <pre>  <code class="language-objectivec">    //模拟数据      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];  </code></pre>    <p>2.异步方式</p>    <pre>  <code class="language-objectivec">    //模拟数据      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");          }      }];  </code></pre>    <h3>YYCache缓存LRU清理</h3>    <p>LRU(Least Recently Used)算法大家都比较熟悉,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉。接下来我们测试一下</p>    <pre>  <code class="language-objectivec">    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秒  </code></pre>    <p>模拟一下清理</p>    <pre>  <code class="language-objectivec">   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);          }        });  </code></pre>    <p>YYCache和PINCache一样并没有实现基于最大内存开销进行LRU,不过YYCache实现了最大缓存数据个数进行LRU清理,这一点也是选择YYCache原因之一,对于YYCache磁盘LRU清理并不是及时清理,而是后台开启一个定时任务进行RLU清理操作,定时时间默认是60s。</p>    <h3>YYCache与PINCache对比</h3>    <p>对于我这里的使用场景大部分用于缓存json字符串,我这里就以存储字符串来对比一下写入与读取效率</p>    <p>1.写入性能对比</p>    <p>YYCache</p>    <pre>  <code class="language-objectivec">    //模拟数据      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);  </code></pre>    <p>运行结果</p>    <p><img src="https://simg.open-open.com/show/9febe906f1b786770e2d56108de1d008.png"></p>    <p>PINCache</p>    <pre>  <code class="language-objectivec">     //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);  </code></pre>    <p>运行结果</p>    <p><img src="https://simg.open-open.com/show/0d562a6e427059ee9f260759eadb339c.png"></p>    <p>通过上面的测试可以看出 同样大小的数据,无论同步方式还是异步方式,YYCache性能都要由于PINCache。</p>    <p>2.读取性能对比</p>    <p>YYCache</p>    <pre>  <code class="language-objectivec">    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);  </code></pre>    <p>运行结果:</p>    <p><img src="https://simg.open-open.com/show/f92f91a335e7b4ca2c46da80a955452b.png"></p>    <p>PINCache</p>    <pre>  <code class="language-objectivec">  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);  </code></pre>    <p>运行结果:</p>    <p><img src="https://simg.open-open.com/show/2b737dc9436e0cdec1bdaa3ee4254d58.png"></p>    <p>通过运行结果,在读取方面YYCache也是优于PINCache。</p>    <h3>总结:</h3>    <p>经过一番查阅资料和自己写例子测试,最终项目中决定使用YYCache进行缓存管理。</p>    <p> </p>    <p>来自:http://www.cnblogs.com/whoislcj/p/6429108.html</p>    <p> </p>