线程安全的iOS通用缓存-SwiftlyCache

iOS开发中或多或少都会使用到Cache来减少网络请求,在网络上也有很多使用Objective-c开发的Cache框架,而Swift开发的Cache框架相对来说就要少一些,所以我就用Swift 5开发了一款Cache库-SwiftlyCache(https://github.com/hlc0000/SwiftlyCache)

特性:

  • 支持所有遵守Codable协议的数据类型
  • 支持LRU淘汰算法
  • 当收到内存警告或者App进入后台时,内存缓存可以配置为自动清空或者手动清空
  • 支持使用Subscript,使读写数据更加方便
  • 提供了MultiCacheGenneratorMemoryCacheGeneratorDiskCacheGenerator用于支持for..incompactMapmapfilter等方法

架构图:

image

成员职责划分:

CacheAware:提供了一些基本接口的协议,MultiCache,MemoryCache,DiskCache等都遵守该协议. 
MultiCache:多重缓存,调用MemoryCache以及DiskCache相关方法. 
MemoryCache:负责处理容量小,相对高速的内存缓存,线程安全,支持异步操作,支持自动和手动清理缓存功能. 
MemoryStorage:MemoryCache使用的双向链表类. 
LinkedNode:双向链表节点类. 
DiskCache:负责处理容量大,相对低速的磁盘缓存,线程安全,支持异步操作,自动和手动清理缓存功能. 
DiskStorage:DiskCache内部实现类. 
DiskStorageItem:用于加载磁盘缓存数据使用的. 
SwiftlyCache支持使用Subscript,读写更加方便. 
SwiftlyCache提供了MultiCacheGenerator、MemoryCacheGenerator、DiskCacheGenerator用于支持for... in、compactMap、 map、filter等一系列方法

使用方法:

CocoaPods:

1.在Podfile中添加pod SwiftlyCache
2.执行pod install或者pod update
3.导入 SwiftlyCache

手动导入:

1.下载SwiftlyCache文件夹内所有内容
2.将SwiftlyCache内的源文件添加到你的工程

属性的使用:

MemoryCache可供使用的属性:

设置最大的内存缓存容量(0为不限制)

public var totalCostLimit:vm_size_t = 0

设置最大的内存缓存数量

public var totalCountLimit:vm_size_t = 0

系统内存警告是否删除所有内存数据,默认为true

public var autoRemoveAllObjectWhenMemoryWarning =true

进入后台是否删除所有内存数据,默认为true

public var autoRemoveAllObjectWhenEnterBackground =true

DiskCache可供使用的属性:

设置最大的磁盘缓存容量(0为不限制)

public var maxSize:vm_size_t = 0

设置最大的磁盘缓存数量

public var maxCountLimit:vm_size_t = 0

缓存的过期时间(默认是一周)

public var maxCachePeriodInSecond:TimeInterval = 60 * 60 * 24 * 7

自动检查时间设置(默认为120秒)

自动检查磁盘缓存是否达到限制,如果达到限制,则自动丢弃部分数据(缓存最大容量限制、缓存最大个数限制、数据是否过期)

public var autoInterval:TimeInterval = 120

接口的使用:

MultiCacheMemoryCache,DiskCache中的设置缓存、获取缓存、根据key查询是否存在对应的缓存数据、移除全部缓存数据、根据key移除对应的缓存数据都是遵守CacheAware协议的

设置缓存对象:(Value为所有遵守Codable协议的数据类型)

public funcset(forKey key: String, value: Value?, cost: vm_size_t = 0)->Bool
public funcset(forKey key:String,value:Value?,cost:vm_size_t = 0,completionHandler:@escaping((_ key:String,_ finished:Bool) -> Void))

可以通过Subscript的形势设置缓存keyValue

cache["key"] = Value

获取缓存对象

public func object(forKey key: String) -> Value?public func object(forKey key:String,completionHandler:@escaping((_ key:String,_ value:Value?) -> Void))

也可以通过Subscript的方式获取对应的缓存对象

let object = cache["key"]

根据给定的key查找缓存中是否存在对应的Value

public func isExistsObjectForKey(forKey key: String) -> Bool
public func isExistsObjectForKey(forKey key:String,completionHandler:@escaping((_ key:String,_ contain:Bool) -> Void))

根据key移除缓存中对应的value

public func removeObject(forKey key: String)
public func removeObject(forKey key: String, completionHandler: @escaping (() -> Void))

移除所有缓存

public func removeAll()public func removeAll(completionHandler:@escaping(() -> Void)

也可以通过for ... incompactMapmapfilter等方式获取到对应的缓存数据

public func makeIterator() -> MultiCacheGenerator
let iterator = cache.makeIterator()
while let result = iterator.next(){}
let flatMapResult = cache.compactMap{$0}
print("flatMapResult:\(flatMapResult)")
let filterResult = cache.filter{(key,object) -> Bool in return key =="shirley2"}
print("filterResult:\(filterResult)")
cache.forEach(print($0) )
let values = cache.map{return$0}
print("value:\(value)")
for(key,object) in cache{
  print("key:\(key),object:\(object)")
}

MultiCacheMemoryCache所有的可供使用的接口就介绍完了,DiskCache除了上述所有接口之外还有以下几个:

移除所有过期数据

public func removeAllExpired()->Bool{}

获取磁盘缓存总个数

public func getTotalItemCount()->Int}
public func getTotalItemCount(completionHandler:@escaping((_ count:Int)->Void)){}

获取磁盘缓存总占用容量

public func getTotalItemSize()->Int32{}
public func getTotalItemSize(completionHandler:@escaping((_ size:Int32)->Void)){}

性能对比:

之前也看过一些用Objective-c开发的Cache框架,比如PINCache,YYCache等,也基本了解了他们的一些优缺点,所以在SwiftlyCache中也尽量融合了他们的一些优点.

单线程下的MemoryCache性能测试(150000次)

image

PINMemoryCache写入数据时采用三个字典的方式分别记录缓存对象、缓存时间、缓存容量,在每次写入数据时都需要依次对三个字典进行写入操作.

YYMemoryCacheSwiftlyCache在每次写入数据的时候最多只需要对字典进行一次写入操作.

在每次缓存数据完成之后,都需要丢弃超出TotalCountTotalCost的数据,PINMemoryCache在淘汰时都需要对Date字典重新进行排序,然后再丢弃掉最老的数据.

YYCacheSwiftlyCache则需要每次从链表的最后开始移除,YYCachecost淘汰是异步线程中进行的,而SwiftlyCache则是在当前线程中进行(每一次设置缓存数据完成后都会对TotalCost进行判断,可丢弃数据很少,如果使用异步线程的开销蛮大的).

单线程下的DiskCache性能测试(1000次,左侧数据为10k,右侧数据为100k)

image

PINDiskCache使用文件缓存数据,设置文件参数,文件的大小来管理缓存数据,对缓存数据的增删改查也是转化为对文件的读写删除操作.

YYDiskCacheSwiftlyCacheDiskCache都是使用SQLite和文件结合的方式进行数据缓存,可以更好得扩展元数据,实现LRU淘汰算法,当缓存数据超过20k,将元数据写入数据库,value写在文件中.

最后的话:

终于完成了最终版本的发布了,我终于可以去放肆的抽上一根烟了,哈哈哈,也希望各位小伙伴多多提出宝贵意见和建议....谢谢啦啦啦啦啦啦.......

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 2、对查询进行优...
    yaogx阅读 300评论 0 0
  • 首先要了解什么是CSS 预处理器? SCSS是一种CSS预处理语言 定义了一种新的专门的编程语言,编译后形成正常的...
    _往后_阅读 361评论 0 1
  • 报错原因:项目外层的目录名发生更改解决办法:1.删除项目中node_modules文件夹 ,2.执行 npm ca...
    早起早起早起up阅读 479评论 0 0
  • 监听器 (一)监听器的概述 1、什么是监听器监听器就是一个实现了特定接口的Java类,这个Java类用于监听另一个...
    唐梦帆阅读 134评论 0 0
  • 声东击西妹妹梗 现在是花盛开的季节,有很多桂花飘香的味道,外面的花衣裳一大堆,以前都是地上一大堆落叶,相互不要散,...
    不像话的故事阅读 63评论 0 0