- 作为一个iOS开发你也许不知道图片的内存管理机制、也不不知道图片的解析机制、但是你肯定用过SDWebImage,也许用过Kingfisher。
- 大多数人习惯了只要加载图片都用第三方, 但是你真正了解第三方为你做了什么吗?为什么我们不自己去下载图片,并且管理呢?
也许你看完这篇文章心里就会有个答案
我们就从源码开始说起吧:
-
首先,我们就一起分析一下该框架的组成。
将KF导入工程后,下面是其结构:项目结构
除去support Files, 项目大致分为5个模块:
- 图片存储模块(imageCache)
- 图片下载模块(imageDownloader)
- imageView分类模块(imageView+Kingfisher)
- 图片加载模块(kingfisherManager)
- 缓存key处理模块(String+MD5)
其核心文件是KingfisherManager里面包含了图片赋值策略。
我们从给图片赋值开始看:
// kf是命名空间,swift中方法名不在是前缀加下划线显示而是采用了命名空间形式,跟原生库更接近
let imageV = UIImageView()
imageV.kf.setImage(with: URL(string: self.imageStr), placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
我们创建了一个imageView,然后通过kf调用setImage给imageView赋值,我们点进去看看做了什么
// discardableResult表示返回值可以忽略不会出现警告
@discardableResult
public func setImage(with resource: Resource?,
placeholder: Placeholder? = nil,
options: KingfisherOptionsInfo? = nil,
progressBlock: DownloadProgressBlock? = nil,
completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
{
guard let resource = resource else {
self.placeholder = placeholder
setWebURL(nil)
completionHandler?(nil, nil, .none, nil)
return .empty
}
// options是个数组,存储了关于图片加载的配置
var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil
if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet.
self.placeholder = placeholder
}
// 开启加载动画
let maybeIndicator = indicator
maybeIndicator?.startAnimatingView()
setWebURL(resource.downloadURL)
if base.shouldPreloadAllAnimation() {
options.append(.preloadAllAnimationData)
}
// 真正加载图片的方法
let task = KingfisherManager.shared.retrieveImage(
with: resource,
options: options,
progressBlock: { receivedSize, totalSize in
guard resource.downloadURL == self.webURL else {
return
}
if let progressBlock = progressBlock {
progressBlock(receivedSize, totalSize)
}
},
completionHandler: {[weak base] image, error, cacheType, imageURL in
DispatchQueue.main.safeAsync {
maybeIndicator?.stopAnimatingView()
guard let strongBase = base, imageURL == self.webURL else {
completionHandler?(image, error, cacheType, imageURL)
return
}
self.setImageTask(nil)
guard let image = image else {
completionHandler?(nil, error, cacheType, imageURL)
return
}
guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)),
case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
{
self.placeholder = nil
strongBase.image = image
completionHandler?(image, error, cacheType, imageURL)
return
}
#if !os(macOS)
UIView.transition(