这篇文章已经对Kingfisher的源码分析的非常好了:
Kingfisher源码阅读
以下仅是自己一些学习笔记:
associate object
由于extension中不能定义存储型变量,这里需要使用的是associate object去在底层替代一个存储型变量的作用。在UIImageView的extension中分别对需要读取的url地址,读取过程中是否使用indicator的设置属性,indicator控件通过这样的方式进行了实现。
设置存储的时候,以存储url为例,除去要指明关联的源对象(self),所关联的对象(设置时为url地址,删除时为nil)之外,还有一个关联的key为一个void指针和关联策略,指针的用法在KF中是:
private var lastURLKey : Void?
而在另一篇nshipster的介绍中则是专门为这些key封装了一个结构体,同时在内部用静态变量作为key:
extension UIViewController {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
}
而关联策略也需要在枚举中选择,这里的选项OBJC_ASSOCIATION_RETAION_NONATOMIC应该也是比较常见的情况。
cancel
kf_setImageWithResource...系列的API统一返回了一个RetrieveImageTask的对象,来封装获取图像的过程(无论是从网络还是从本地存储),这里主要提供的功能就是为了做cancel的操作。
/// RetrieveImageTask represents a task of image retrieving process. It contains an async task of getting image from disk and from network.
public class RetrieveImageTask {
// If task is canceled before the download task started (which means the `downloadTask` is nil),
// the download task should not begin.
var cancelled: Bool = false
var diskRetrieveTask: RetrieveImageDiskTask?
var downloadTask: RetrieveImageDownloadTask?
/**
Cancel current task. If this task does not begin or already done, do nothing.
*/
public func cancel() {
// From Xcode 7 beta 6, the `dispatch_block_cancel` will crash at runtime.
// It fixed in Xcode 7.1.
// See https://github.com/onevcat/Kingfisher/issues/99 for more.
if let diskRetrieveTask = diskRetrieveTask {
dispatch_block_cancel(diskRetrieveTask)
}
if let downloadTask = downloadTask {
downloadTask.cancel()
}
cancelled = true
}
}
这里RetrieveImageDiskTask代表了一个去存储中获取数据的操作,其定义是一个dispatch_block_t,在实现中通过dispatch_block_create创建,并且立刻通过dispatch_async进入异步队列,但是这里返回引用可以支持做cancel操作。
另外一个RetrieveImageDownloadTask代表的则是网络请求的Task,它是NSURLSessionDataTask的别名
在Kingfisher的demo中并没有用到cancel功能,但是一个健全强大的异步加载图像的库还是必须支持这样的功能,尤其是当图像以grid的方式在界面上展示的时候,如果用户快速滑动,代码实现就必须能够做一些预加载和取消无效任务的优化来保证性能。
singleton
swift的单例现在只要写成这样就可以了,比起objc时代的dispatch_once还是方便不少
private let instance = KingfisherManager()
public class KingfisherManager{
public class var shareManager : KingfisherManager{
return instance
}
}
manager, cache, downloader
由于图像异步加载中存在各种需求可能,因此KF也是提供了多种配置选项,但是在代码实现中,单例的管理者Manager,存储管理的cache和下载管理的downloader逻辑非常清晰,各自负责的代码都进行了隔离,当然它们之间在流程上用了大量的block传递,这个还要多看几遍学习...
KingfisherOptionsInfo
这个类写的实在是太华丽了,第一次看到这段代码代码完全不明所以...
if let optionsItem = optionsInfo.kf_firstMatchIgnoringAssociatedValue(.Options(.None)), case .Options(let optionsInOptionsInfo) = optionsItem {
..........
}
其实这个设置的写法确实是很巧妙的,首先这里不同类型的配置项用带参数的枚举来定义:
public enum KingfisherOptionsInfoItem {
case Options(KingfisherOptions)
case TargetCache(ImageCache)
case Downloader(ImageDownloader)
case Transition(ImageTransition)
}
KingfisherOptions又是用Swift的OptionSetType定义的,这样就可以方便的使用union, intersect, exclusiveOr, contains, insert, remove等方法
整个配置是一个枚举的数组,这样的定义非常的清晰,而kf_firstMatchIgnoringAssociatedValue的方法则是一个CollectionType extension的泛型方法,进而用一个重载的操作符去判定是否为某种枚举的case
infix operator <== {
associativity none
precedence 160
}
// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
switch (lhs, rhs) {
case (.Options(_), .Options(_)): return true
case (.TargetCache(_), .TargetCache(_)): return true
case (.Downloader(_), .Downloader(_)): return true
case (.Transition(_), .Transition(_)): return true
default: return false
}
}
extension CollectionType where Generator.Element == KingfisherOptionsInfoItem {
func kf_firstMatchIgnoringAssociatedValue(target: Generator.Element) -> Generator.Element? {
let index = indexOf {
item in
return item <== target
}
return (index != nil) ? self[index!] : nil
}
}
结合源码分析读完整个代码后感受到了代码的优雅,写的真是太好了!