使用 KVFetcher
可轻松获取、自动缓存甚至预获取耗时加载的项目,如从照片库或互联网获取图像、从磁盘读取文件或执行一些复杂的计算。
内容
要求
- iOS 10.0+
- Xcode 10.0+
- Swift 4.2+
特性
🅿️ 以协议为核心✳️ 易于子类化和使用的 KVFetcher 类📥 自动缓存获取值的缓存版本📤 删除旧的缓存值以为新值腾出空间⏳ 主动版本,提前获取值(预获取)
类
KVFetcher 类
提供三种可用的版本
- Fetcher :
KVFetcher<Key, Value>
基础类 - 缓存 Fetcher:
KVFetcher<Key, Value>.Caching
自动缓存特性 - 主动 Fetcher:
KVFetcher<Key, Value>.Caching.Active
可提前预取/预缓存量
Key
:获取值时使用的标识符,Value
:获取执行的结果。 ValueCompletion!
= ((Value)->Void)!
完成处理闭包
注意:你应该通过继承这些类并覆写获取执行方法来创建你自己的定制化获取器。
KVCacher 类
将获取的值放入一个字典中,并追踪一个值(对于一个键)已添加的时间。它的存储空间可以是有限的,因此它可能自动删除较旧的条目为新条目腾出空间。缓存的值的最大年龄也可以设置。
注意:如果你想在缓存的/主动的获取器中使用自定义缓存器,请使用
.CustomCached<Cacher: KVCacher>
而不是.Cached
。这是一个泛型,其中你可以定义缓存器的类型。
限制
定义缓存器的存储限制。它可以简单地通过计数或检查使用内存来实现。在后一种情况下,必须提供转换块来确定给定值(或键)的内存占用。
示例:一个 UIImage 对象的内存占用可以通过其像素尺寸来估算。如果将要缓存许多图像且它们的尺寸大致相同,那么缓存器的存储可以通过它们的数量来简单地限制。
协议
如果你不是很喜欢继承或者需要创建更定制的获取器,你可以在你的自定义获取器中实现这些协议
KVFetcher_Protocol
KVFetcher_Caching_Protocol
KVFetcher_Caching_Active_Protocol
对于你的自定义缓存器也是如此
KVCacher_Protocol
使用示例:缓存 fetcher
FlagFetcher 是一个缓存的 fetcher,通过 ISO 字符串获取某个国家国旗的图片。它使用 countryflags.io API。例如,"de" 会获取并缓存德国国旗
FlagFetcher 是一个简单的 KVFetcher.Cached
类的子类,以 String 和 UIImage 作为键/值。它将自动缓存每个获取的值。只需重写 _executeFetchValue(for:completion:)
方法
注意:值是
UIImage?
(可选),因为我们无法100% 确保网络图片的获取会成功。
class FlagFetcher: KVFetcher<String, UIImage?>.Caching {
override func _executeFetchValue(for key: String, completion: ValueCompletion!) {
let url = URL(string: "https://www.countryflags.io/\(key)/shiny/64.png")!
URLSession.shared.dataTask(with: url) { data, _, _ in
DispatchQueue.main.async {
guard let data = data else {
print("Couldn't fetch data from internet")
return completion(nil)
}
guard let image = UIImage(data: data) else {
print("Fetched data is not image")
return completion(nil)
}
completion(image)
}
}.resume()
}
}
创建实例
让我们创建一个可以缓存 100 张国旗图片的 FlagFetcher 类实例。它只有一个属性:cacher(KVCacher
let flagFetcher = FlagFetcher(cacher: .init(limit: .count(max: 100)))
获取值
获取(并自动缓存)通过使用 fetchValue(for:,completion:)
方法完成。让我们通过传递 "de"
作为键来获取德国国旗图像
flagFetcher.fetchValue(for: "de", completion: { flagImage in
guard let flagImage = flagImage else {
return print("Couldn't fetch flag image for Germany!")
}
print("Got flag image: \(flagImage)!")
})
注意:
fetchValue(for:completion:)
方法是异步的,值在 completion 闭包中返回。一旦获取,结果值就会被缓存,下次尝试获取 "de" 的值时,闭包将同步执行。
使用示例:活动缓存 fetcher
想象一下用户查看世界国旗的网页相册。例如,我们可以预获取从某个国旗开始的一个国旗范围,直到总计5个国旗。例如,当查看国旗31时,国旗30、32、33和34将被预获取。
如何实现?只需将 FlagFetcher 的父类更改为它的 .Active 版本!现在它已成为一个活动缓存 fetcher。
+⇣
class FlagFetcher: KVFetcher<String, UIImage?>.Caching.Active {
现在除 cacher 外,FlagFetcher 还有三个其他属性
keys:
所有国旗代码的列表currentIndex:
当前查看国旗的初始索引options:
(预获取的范围、偏移和方向)
class FlagViewer: UIViewController {
var activeFlagFetcher: FlagFetcher!
var flagList = ["FI", "AT", "BE", "HR", "FR", "DE", "GR", "EE", "FR"]
override func viewDidLoad() {
activeFlagFetcher = .init(keys: flagList,
currentIndex: 0,
options: .init(range: 5, offset: 0, direction: .upcoming),
cacher: .unlimited)
activeFlagFetcher.startPrefetching()
}
@IBAction func userMovedOntoNextFlag() {
activeFlagFetcher.currentIndex += 1
displayNextFlag()
//...
}
//...
}
需要调用 startPrefetching()
来开始预取标记。每次用户滑动标记时,都需要更新 currentIndex
属性,这样我们的活动获取器就会知道哪些标记需要预缓存。要停止预取,只需调用 stopPrefetching()
方法即可。
更多实用示例
- PHAsset → UIImage 获取器(照片)
- CLLocation → CLPlacemark 获取器(核心定位)
一些值得拥有的特性
同步获取
使用 .fetchSynchronously(_:)
来同步获取值(阻塞主线程直到返回值)。当执行方法内部的操作是同步的或者你已经在后台线程时,这是一个不错的用法。
let flagImage = flagFetcher.fetchSynchronously("de") // returns UIImage?
print("Got flag image with size: \(flagImage!.size)")
使用索引器 [ ]
同步获取的一种简短方法——使用方括号。请注意,这会导致对值进行强制展开!
let germanFlag = flagFetcher["de"] // returns UIImage
print("Got flag image with size: \(germanFlag.size)")
获取多个值
使用 .fetchMultiple(keys:completion:)
方法来获取多个值。所有值获取完毕后,将调用完成闭包(处理器)。
同步
类似于上面,使用.fetchMultipleSynchronously(keys:)
来异步获取多个值(阻塞主线程,直到返回值)。
使用下标[]
为了避免下标内包含数组(看起来杂乱),只能使用范围作为下标以获取多个值。为了让事情简单,键必须是Int类型。例如,这是一个从PHFetchResult<PHAsset>
对象获取资源的获取器。
let myAssets: [PHAsset] = assetFetcher[0..<100] // returns array of PHAsset ready to use
手动从缓存中检索值
在缓存检索获取器的cacher
属性上使用.cachedValue(for:)
来从缓存中检索一个值(前提是之前已经获取/缓存过)。如果它不存在,则返回nil。使用.has(cachedValueFor:)
来检查是否存在。
if let germanFlag = flagFetcher.cacher.cachedValue(for: "de"){
print("🇩🇪 exists in the cache! \(germanFlag.size)")
}
手动保存值到缓存
使用.cache(_:for:)
方法对cacher
属性进行操作,以将值保存到缓存中。获取值后不需要使用此方法,因为当使用具有KVFetcher.Cached子类的值进行获取时,它将自动进行缓存。
let germanFlag = UIImage(named: "germany.png")!
flagFetcher.cacher.cache(germanFlag, for: "de")
安装
CocoaPods
CocoaPods是Cocoa项目的依赖关系管理器。要将KVFetcher集成到Xcode项目中,使用CocoaPods,请在您的Podfile
中指定它。
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'KVFetcher', '~> 0.9.0'
end
然后在Terminal中运行pod install
命令。
Carthage
Carthage 是一个去中心化的依赖管理器,它可以构建您的依赖项,并提供二进制框架。要使用 Carthage 将 KVFetcher 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它。
github "manuelvrhovac/KVFetcher" ~> 0.8.0
运行 carthage update
来构建框架,并将构建好的 KVFetcher.framework
拖拽到您的 Xcode 项目中。
手动集成
如果您不希望在项目中使用上述任一依赖管理器,可以手动将 KVFetcher 集成到项目中。
许可证
KVFetcher 在 MIT 许可下发布。详细信息请查看 LICENSE 文件。