缓存
5大方法:增加,获取,删除指定,删除全部,是否存在(contains OR exists),用cnt,size和age来限制。
自定义缓存
<1> 选择一个合适的缓存键 - 缓存键用来做图片的唯一标识。如果实时创建图片,通常不太好生成一个字符串来区分别的图片。我们可以用图片的文件名或者表格索引。
<2> 提前缓存 - 如果生成和加载数据的代价很大,你可能想当第一次需要用到的时候再去加载和缓存。提前加载的逻辑是应用内在就有的,对于一个给定的位置和滚动方向,我们就可以精确地判断出哪一张图片将会出现。
<3> 缓存失效(时间戳)- 如果图片文件发生了变化,怎样才能通知到缓存更新呢?这是个非常困难的问题,但是幸运的是当从程序资源加载静态图片的时候并不需要考虑这些。对用户提供的图片来说(可能会被修改或者覆盖),一个比较好的方式就是当图片缓存的时候打上一个时间戳以便当文件更新的时候作比较。
<4> 缓存回收 - 当内存不够的时候,如何判断哪些缓存需要清空呢?这就需要到你写一个合适的算法了。对缓存回收的问题,苹果提供了一个叫做NSCache通用的解决方案
AFAutoPurgingImageCache实际是NSDcit+变量控制的缓存(当接收到内存警告通知 -> removeAllImages)
(容量 - 当前内存用量 - identifier - lastAccessDate)
<1> 封装一个AFCachedImage对象,用cachedImages字典来存储。
<2> 主要方法:
-addImage:withID - 覆盖之前ID的cachedImage。如果超出容量,则清除最早访问时间的图片。removeID/All,Get。
-addImage:forRequest中用request.URL.absoluteString+additionalID做图片的ID。
YYCache
`YYCache`是一个线程安全的KV缓存
`YYMemoryCache`存储对象在一个小而快的内存缓存
`YYDiskCache`持久化对象在一个大而慢的磁盘缓存
对YYMemoryCache 和 YYDiskCache的简单方法封装。
YYMemoryCache
API和性能与`NSCache`类似,与其不同:
<1> 使用LRU移除对象,NSCache的移除方法是非确定的
<2> 能够控制cost,count和age。NSCache的限制不准确
<3> 当获得内存警告或者app进入后台(默认,removeAll),能自动配置移除对象。
_YYLinkedMap - _YYLinkedMapNode
LRU 是用双向链表(保持尾部时间最久)配合 NSDictionary 实现的,增、删、改、查、清空的时间复杂度都是 O(1)
把node的key和value存入LinkedMap内部的CF字典(_totalCost+_totalCnt)中,实现O(1)的数据访问。双向链表采用头插法,每次获取node,会更新_time,并bringNodeToHead。同时,如果setObj时,如果node结点存在,也会bringNodeToHead
5s递归循环的后台Low优先级队列。5s后异步串行的执行_trimCost(循环去尾,放入holderArr,异步后台队列释放内存)/Count同/Age同。
YYKVStorage
一般,写数据到sqlite比到外部文件要快,但是读数据的性能取决于数据的大小。测试中,当数据超过20KB时,从外部文件读数据要比sqlite快。
<1> 如果要存储大量小数据(如联系人缓存),用YYKVStorageTypeSQLite来获得更好的性能
<2> 如果要存储大文件(如图片缓存),用YYKVStorageTypeFile获得更好的性能
<3> 用YYKVStorageTypeMixed,为每个item选择你的存储类型
初始化后,基于`path`创建一个目录来持有key-value data。打开db,建表。
_dbStmtCache缓存(key - sqlString,value - sqlite3_stmt)
<1> 直接操作sqlite3的CRUD。
<2> file的CRUD
YYDiskCache
线程安全的缓存,存储键值对,通过SQLite和文件系统(类似NSURLCache的磁盘缓存)
* LRU删除对象
* 控制cost, count, and age
* 当没有空闲磁盘空间,通过配置自动删除对象
* 为所有对象自动决定存储类型(sqlite/file)以获得更好的性能
<1> 维护一个全局的mapTable缓存(key - cache.path,value - cache)
60s递归循环的后台Low优先级队列。60s后异步并发的执行_trimCost(_kv方法)/Count同/Age同/FreeDiskSpace(调用_trimCost)。
<2> 初始化设置时,有一个InlineThreshold,0表示所有对象都将以单独的文件存储,NSUIntegerMax表示所有的对象都存储于sqlite。
<3>
objectForKey - 从数据库获取YYKVStorageItem对象,将item.value解档为id对象,有extendedData会设置为id对象的关联对象。
setObject:forKey - 尝试获取extendedData,将id对象归档为NSData,当_type不为SQlite且data长度>InlineThreshold,就通过key生成filename。最后调用_kv方法
这里,通过key生成filename,如果有自定义filenameBlock就调用,没有就对String编码后进行MD5。
其余都是加锁调用_kv方法。
SDImageCache
SDImageCache维护一个内存cache和一个可选的磁盘缓存。磁盘缓存写操作被异步执行,因此它不会延迟UI。
<1> 内存缓存使用AutoPurgeCache,AutoPurgeCache继承NSCache,监听内存警告通知,触发removeAll方法。
ImageDataHasPNGPreffix提供了检测PNG图片的方法(PNG头8个字节唯一)
对于由key获取filename,同样提供了MD5的方法cachedFilenameForKey
<2>
监听DidReceiveMemoryWarning通知 = 调用removeAll清空内存缓存。WillTerminate通知 = cleanDisk。DidEnterBackground通知 = 后台任务过期Block 。cleanDisk,回调结束任务Block。
storeImage:recalculateFromImage:imageData:forKey:toDisk - 内存缓存 + 异步串行磁盘缓存 = 判断PNG或JPEG保存为data,存储data(diskCache+MD5的keyString)。
内存缓存图片从NSCache直接获取。磁盘缓存图片,首先从默认和自定义路径搜索,获取data,根据data创建image并按scale调整,最后提前解码并返回。
clearDisk - removeItem+新建目录。
cleanDisk - 异步串行队列 - 首先移除一周前的缓存文件, 然后若还超过MaxCacheSize,则清除oldest文件,直到size小于max的一半。