Last active
April 26, 2021 01:42
-
-
Save LieonShelly/30a8187d035837e7136e2ad4d85e2417 to your computer and use it in GitHub Desktop.
Kingfisher源码解读,代码片段
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///ImageView+Kingfisher.swift | |
/// 设置图片内部方法 | |
func setImage( | |
with source: Source?, | |
placeholder: Placeholder? = nil, | |
parsedOptions: KingfisherParsedOptionsInfo, | |
progressBlock: DownloadProgressBlock? = nil, | |
completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask? | |
{ | |
var mutatingSelf = self | |
guard let source = source else { | |
mutatingSelf.placeholder = placeholder | |
mutatingSelf.taskIdentifier = nil | |
completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource))) | |
return nil | |
} | |
var options = parsedOptions | |
// 判断是否设置站位图 | |
let isEmptyImage = base.image == nil && self.placeholder == nil | |
if !options.keepCurrentImageWhileLoading || isEmptyImage { | |
// Always set placeholder while there is no image/placeholder yet. | |
mutatingSelf.placeholder = placeholder | |
} | |
// 判断是否有加载UI | |
let maybeIndicator = indicator | |
maybeIndicator?.startAnimatingView() | |
// 生成标志符号 | |
let issuedIdentifier = Source.Identifier.next() | |
// 绑定标识符 | |
mutatingSelf.taskIdentifier = issuedIdentifier | |
// 判断是否显示预加载动画 | |
if base.shouldPreloadAllAnimation() { | |
options.preloadAllAnimationData = true | |
} | |
// 设置进度回调,如果有的话 | |
if let block = progressBlock { | |
options.onDataReceived = (options.onDataReceived ?? []) + [ImageLoadingProgressSideEffect(block)] | |
} | |
// 判断是否设置进度显示的UI | |
if let provider = ImageProgressiveProvider(options, refresh: { image in | |
self.base.image = image | |
}) { | |
options.onDataReceived = (options.onDataReceived ?? []) + [provider] | |
} | |
options.onDataReceived?.forEach { | |
$0.onShouldApply = { issuedIdentifier == self.taskIdentifier } | |
} | |
// 获取图片,监听回调 | |
let task = KingfisherManager.shared.retrieveImage( | |
with: source, | |
options: options, | |
downloadTaskUpdated: { | |
mutatingSelf.imageTask = $0 | |
}, | |
completionHandler: { result in | |
CallbackQueue.mainCurrentOrAsync.execute { | |
// 停止加载动画,如果有的话 | |
maybeIndicator?.stopAnimatingView() | |
// 判断当前任务id与回调的任务Id是否是同一个, 如果不是就是不当前控件的图片 | |
guard issuedIdentifier == self.taskIdentifier else { | |
let reason: KingfisherError.ImageSettingErrorReason | |
do { | |
let value = try result.get() | |
reason = .notCurrentSourceTask(result: value, error: nil, source: source) | |
} catch { | |
reason = .notCurrentSourceTask(result: nil, error: error, source: source) | |
} | |
let error = KingfisherError.imageSettingError(reason: reason) | |
completionHandler?(.failure(error)) | |
return | |
} | |
// 清除当前任务 | |
mutatingSelf.imageTask = nil | |
// 清除当前任务Id | |
mutatingSelf.taskIdentifier = nil | |
switch result { | |
case .success(let value): | |
// 是否有设置转场动画, 如果没有直接设置图片 | |
guard self.needsTransition(options: options, cacheType: value.cacheType) else { | |
mutatingSelf.placeholder = nil | |
self.base.image = value.image | |
completionHandler?(result) | |
return | |
} | |
// 开始转场动画,并设置图片 | |
self.makeTransition(image: value.image, transition: options.transition) { | |
completionHandler?(result) | |
} | |
case .failure: | |
// 加载失败,判断是否有设置失败的图片,如果有设置,则显示失败站位图 | |
if let image = options.onFailureImage { | |
self.base.image = image | |
} | |
completionHandler?(result) | |
} | |
} | |
} | |
) | |
// 为当前控件绑定下载任务 | |
mutatingSelf.imageTask = task | |
return task | |
} | |
/// KingfisherManager.swift | |
private func retrieveImage( | |
with source: Source, | |
context: RetrievingContext, | |
completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask? { | |
let options = context.options | |
/// 判断是否需要强制刷新 | |
if options.forceRefresh { | |
/// 加载并缓存图片 | |
return loadAndCacheImage( | |
source: source, | |
context: context, | |
completionHandler: completionHandler)?.value | |
} else { | |
/// 从缓存中获取图片 | |
let loadedFromCache = retrieveImageFromCache( | |
source: source, | |
context: context, | |
completionHandler: completionHandler) | |
/// 在缓存中获取到了数据,直接返回 | |
if loadedFromCache { | |
return nil | |
} | |
if options.onlyFromCache { | |
let error = KingfisherError.cacheError(reason: .imageNotExisting(key: source.cacheKey)) | |
completionHandler?(.failure(error)) | |
return nil | |
} | |
/// 缓存中没有数据,则开始加载数据并缓存 | |
return loadAndCacheImage( | |
source: source, | |
context: context, | |
completionHandler: completionHandler)?.value | |
} | |
} | |
// ImageDownloader.swift | |
/// 开始下载任务 | |
private func startDownloadTask( | |
context: DownloadingContext, | |
callback: SessionDataTask.TaskCallback | |
) -> DownloadTask | |
{ | |
/// 添加下载任务 | |
let downloadTask = addDownloadTask(context: context, callback: callback) | |
let sessionTask = downloadTask.sessionTask | |
// 如果任务已经开始,则返回 | |
guard !sessionTask.started else { | |
return downloadTask | |
} | |
sessionTask.onTaskDone.delegate(on: self) { (self, done) in | |
let (result, callbacks) = done | |
// 通知外部已经下载了图片数据 | |
self.reportDidDownloadImageData(result: result, url: context.url) | |
switch result { | |
// 下载完成,开始将二进制数据转换为image | |
case .success(let (data, response)): | |
// 生成图片数据处理器 | |
let processor = ImageDataProcessor( | |
data: data, callbacks: callbacks, processingQueue: context.options.processingQueue | |
) | |
// 监听处理器处理结果回调 | |
processor.onImageProcessed.delegate(on: self) { (self, done) in | |
let (result, callback) = done | |
self.reportDidProcessImage(result: result, url: context.url, response: response) | |
let imageResult = result.map { ImageLoadingResult(image: $0, url: context.url, originalData: data) } | |
let queue = callback.options.callbackQueue | |
queue.execute { callback.onCompleted?.call(imageResult) } | |
} | |
// 开始将二进制数据转换为image | |
processor.process() | |
case .failure(let error): | |
callbacks.forEach { callback in | |
let queue = callback.options.callbackQueue | |
queue.execute { callback.onCompleted?.call(.failure(error)) } | |
} | |
} | |
} | |
/// 通知外部即将下载图片 | |
reportWillDownloadImage(url: context.url, request: context.request) | |
// 开始sessionTask | |
sessionTask.resume() | |
return downloadTask | |
} | |
/// kf前缀(为什么是通过ImageView.kf.setImage的方式调用) | |
/// 定一个Wrapper结构体 | |
public struct KingfisherWrapper<Base> { | |
public let base: Base | |
public init(_ base: Base) { | |
self.base = base | |
} | |
} | |
/// 定义一个协议,什么都不写 | |
public protocol KingfisherCompatible: AnyObject { } | |
public protocol KingfisherCompatibleValue {} | |
/// 为协议扩展一个计算属性(kf的类型为KingfisherWrapper,同时指定KingfisherWrapper的泛型为当前调用者Self) | |
extension KingfisherCompatible { | |
public var kf: KingfisherWrapper<Self> { | |
get { return KingfisherWrapper(self) } | |
set { } | |
} | |
} | |
/// 对应的类型(UIImageView):遵守协议 | |
extension UIImageView: KingfisherCompatible {} | |
/// 扩展定义的结构,指定Base为UIImageView。 这样就相当于UIImageView中有一个计算属性kf,kf可以调用结构体Wrapper里面的方法与属性 | |
extension KingfisherWrapper where Base == UIImageView { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment