Kingfisher 4.x 从使用到源码
如何使用Kingfisher展示图片?
官方Demo中使用setImage展示图片
// source: Kingfisher-Demo > ViewController.swift_ = (cell as! CollectionViewCell).cellImageView.kf.setImage(with: url,placeholder: nil,options: [.transition(ImageTransition.fade(1))],progressBlock: { receivedSize, totalSize inprint("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")},completionHandler: { image, error, cacheType, imageURL inprint("\(indexPath.row + 1): Finished")
})
复制代码
为什么要使用kf
?
Kingfisher.swift
KingfisherCompatible
协议有个只读属性kf
,它返回Kingfisher
。Demo中的cellImageView.kf
即为Kingfisher(ImageView)
。
//source: Kingfisher > Kingfisher.swiftpublic protocol KingfisherCompatible {associatedtype CompatibleTypevar kf: CompatibleType { get }
}public extension KingfisherCompatible {public var kf: Kingfisher<Self> {return Kingfisher(self)}
}复制代码
kingfisher
中,对不同的种类可以进行各种不同的操作,比如对Image,你能对它设置图片、设置动画;对UIButton的不同状态设置图片;还有ImageView、WKInterfaceImage(watchOS中)。我们把这些不同的种类归位Base(泛型),它可以是Image,也可以是UIButton。不可继承的类Kingfisher
就有属性Base
,初始化时传入一个Base
。
//source: Kingfisher > Kingfisher.swiftpublic final class Kingfisher<Base> {public let base: Basepublic init(_ base: Base) {self.base = base}
}
复制代码
//source: Kingfisher > Kingfisher.swiftextension Image: KingfisherCompatible { }
复制代码
在Kingfisher.swift中,让ImageView
实现KingfisherCompatible
协议。
ImageView+Kingfisher.swift
在ImageView+Kingfisher.swift中,定义当Base
时ImageView
的时候,给Kingfisher
拓展方法,例如setImage
方法。
//source: Kingfisher > Extensions > ImageView+Kingfisher.swiftextension Kingfisher where Base: ImageView public func setImage(with resource: Resource?,placeholder: Placeholder? = nil,options: KingfisherOptionsInfo? = nil,progressBlock: DownloadProgressBlock? = nil,completionHandler: CompletionHandler? = nil) -> RetrieveImageTask{// 代码太长,此处省略}复制代码
该setImage
方法主要做了以下事:
1. 如果resource
不存在,比如没有传入图的url
,则给一个默认的placeholder
(占位图)。
2. 如果resource
存在,显示加载动画(若存在),调用KingfisherManager
的retrieveImage
方法,在拉取数据过程中,实时回调当前已收到的资源数据和总数据;完成后,主线程异步变化UI,不再显示加载动画,没有收到image就回调错误,有收到image就将base
(此处为UIImageView
)的image
设为返回的image
。非macOS平台可设置相关动画。
KingfisherManager.swift
该类管理了Kingfisher的下载和缓存,可以使用这个类来从网络URL地址或者缓存获取图片,retrieveImage方法。
//source: Kingfisher > Core > KingfisherManager.swift@discardableResult
public func retrieveImage(with resource: Resource,options: KingfisherOptionsInfo?,progressBlock: DownloadProgressBlock?,completionHandler: CompletionHandler?) -> RetrieveImageTask
{let task = RetrieveImageTask()let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)if options.forceRefresh {_ = downloadAndCacheImage(with: resource.downloadURL,forKey: resource.cacheKey,retrieveImageTask: task,progressBlock: progressBlock,completionHandler: completionHandler,options: options)} else {tryToRetrieveImageFromCache(forKey: resource.cacheKey,with: resource.downloadURL,retrieveImageTask: task,progressBlock: progressBlock,completionHandler: completionHandler,options: options)}return task
}
复制代码
1. 新建获取图片的任务task
(RetrieveImageTask
类型),并读取配置options
(KingfisherOptionsInfo
类型)
2. 如果配置中forceRefresh
(永远从网络读取图片数据)为真,则调用本类中的downloadAndCacheImage
方法,其使用ImageDownloader
的downloadImage
下载图片,下载完成后,如果返回的错误是notModified
,尝试从targetCache
读取已缓存的图片;如果下载成功则使用targetCache(ImageCache)
类型的store
方法缓存图片,然后如果配置中cacheOriginalImage
为真且当前processor
不是DefaultImageProcessor
,使用originalCache
缓存图片。
3. 如果配置中forceRefresh
为假,则调用本类中的tryToRetrieveImageFromCache
方法,其先从缓存targetCache
里找,image
存在则返回;如果image
不存在,且正在使用DefaultImageProcessor
,则去下载图片;如非使用DefaultImageProcessor
,则用originalCache
去看看原始图片是否已在缓存中,如果有则不再下载,直接用processor
处理图片,使用targetCache
存储再返回。
ImageCache.swift
//source: Kingfisher > Core > ImageCache.swiftopen func store(_ image: Image,original: Data? = nil,forKey key: String,processorIdentifier identifier: String = "",cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,toDisk: Bool = true,completionHandler: (() -> Void)? = nil){// 代码太长,此处省略}
复制代码
store
方法给memoryCache
设置key
对应的image
,如果toDisk
(写入磁盘)为真,ioQueue
异步在cache
文件夹下根据key
和data
创建缓存文件(如果cache
文件夹不存在先创建一个cache
文件夹)。主线程异步处理completionHandler
。
ImageDownloader.swift
//source: Kingfisher > Core > ImageDownloader.swiftopen class ImageDownloader {class ImageFetchLoad {var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]()var responseData = NSMutableData()var downloadTaskCount = 0var downloadTask: RetrieveImageDownloadTask?var cancelSemaphore: DispatchSemaphore?}var fetchLoads = [URL: ImageFetchLoad]()//其他属性省略
}复制代码
ImageDownloader
类是根据URL
从服务器下载图片的下载管理器,在fetchLoads
中,不同的URL
对应不同的ImageFetchLoad
,ImageFetchLoad
包含一个URL
中下载的内容(下载进度、处理回调、配置)、返回数据、下载任务数、下载任务、取消信号量。
//source: Kingfisher > Core > ImageDownloader.swift@discardableResult
open func downloadImage(with url: URL,retrieveImageTask: RetrieveImageTask? = nil,options: KingfisherOptionsInfo? = nil,progressBlock: ImageDownloaderProgressBlock? = nil,completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask?{// 代码太长,此处省略}复制代码
downloadImage
方法中,使用URL
和配置option
下载图片。使用URL
作为load
的key
,要开始进程之前要向requestModifier
拿到最终的URL
。可以在该方法中设置下载的timeout
(超时)时长、是否使用HTTP pipelining
。(使用HTTP pipelining
则允许不必等到response
, 就可以再次请求,会很大的提高网络请求的效率)。如果URL
、retrieveImageTask
、modifier
有误,则在回调中显示错误信息,并返回nil
。如果均无误,则调用该类的setup
方法,设置并开启下载任务,fetchLoad
下载任务数加一,retrieveImageTask
的downloadTask
设为setup
方法回调中的fetchLoad
的downloadTask
,后返回下载任务。
//source: Kingfisher > Core > ImageDownloader.swiftfunc setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) {// 代码太长,此处省略}复制代码
setup
方法中,如果URL
传入fetchLoad
方法返回的ImageFetchLoad
存在且当前下载的任务数为0,则【一直等待到下载失败(下载失败会在该类的callCompletionHandlerFailure
方法释放cancelSemaphore
信号量),再尝试下载(调用该方法中的prepareFetchLoad
函数)。】反之:尝试下载(调用prepareFetchLoad
函数)。
prepareFetchLoad
函数中,开启barrier
(等待写入完成后才能读取)同步线程:1.拿到url
对应的ImageFetchLoad
(若空创建新的一个); 2.并对其content
加上一条:callbackPair
(由传入的progressBlock
和completionHandler
所组成)以及当前配置(KingfisherOptionsInfoItem
);3.最后组合成的新ImageFetchLoad
替换旧的url
对应的ImageFetchLoad
。4.session
存在则开启加载url
对应图片的session
。
//source: Kingfisher > Core > ImageDownloader.swiftfunc fetchLoad(for url: URL) -> ImageFetchLoad? {var fetchLoad: ImageFetchLoad?barrierQueue.sync(flags: .barrier) { fetchLoad = fetchLoads[url] }return fetchLoad
}
复制代码
fetchLoad
方法中,开启一个DispatchWorkItemFlags
为barrier
的线程barrierQueue
同步执行以下操作:根据url
拿到fetchLoads
里对应的fetchLoad
,并返回fetchLoad
。
//source: Kingfisher > Core > ImageDownloader.swiftfunc urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {guard let url = task.originalRequest?.url else {return}if let downloader = downloadHolder {downloader.delegate?.imageDownloader(downloader, didFinishDownloadingImageForURL: url, with: task.response, error: error)}guard error == nil else {callCompletionHandlerFailure(error: error!, url: url)return}processImage(for: task, url: url)
}private func processImage(for task: URLSessionTask, url: URL) {// 代码太长,此处省略}复制代码
urlSession(_:task:didCompleteWithError)
方法中,下载失败则使用该类callCompletionHandlerFailure
方法处理失败结果,下载成功则使用processImage(for:url:)
方法处理数据并执行回调,缓存图片数据并刷新界面。开启异步processQueue
(任务并行运行),使用fetchload
中的responseData
得到data
。若downloader
存在delegate,
可在创建UIImage
之前对从url
成功下载的raw image data
再次处理,例如解密(decrypting)或验证(verification)得到data
。缓存处理过的图片数据,如果使用相同的processor
,就不用重复处理该图了。
KingfisherOptionsInfo.swift
KingfisherOptionsInfo.swift文件定义了一些配置。包括缓存,下载优先级,是否每次重新下载图片,是否只从缓存加载图片,在发送下载请求前使用的modifier
(ImageDownloadRequestModifier
类型),下载完的图片处理器processor
(ImageProcessor
类型),在图片显示前修改图片的imageModifier
(ImageModifier
类型)等。
ImageProcessor.swift
ImageProcessor
是一个协议,将下载的数据转换为图片。
DefaultImageProcessor
是默认的Processor
,遵循ImageProcessor
协议,将输入的数据变为有效的图片,支持PNG,JPEG,GIF等格式的图片,如果有Image
,DefaultImageProcessor
会返回图片;有Data
,就将data
转为Image
。
BlendImageProcessor
为图片添加blend
模式。
ImageTransition.swift
可以通过ImageTransition.swift文件来为已下载图片显示添加动画,例如渐入、从上下左右进入等动画,也可自定义动画。
转载于:https://juejin.im/post/5b72a2b06fb9a009a44a9089
Kingfisher 4.x 从使用到源码相关推荐
- Kingfisher源码分析
源码地址: https://github.com/onevcat/Kingfisher Kingfisher是iOS圈内有名的王威写的三方库,参考SDWebImage用Swift语言实现. 三方图片 ...
- 逗视 iOS客户端应用源码
逗视 iOS客户端 支持iPhone,iPad 最新版本1.2.2 简介 在这一个高速运转的社会中,大家真的太忙了,没有了欢笑,没有了生活! 但是我们生活中不能缺少欢乐,搞笑!那么,逗视来了!! 你可 ...
- 高仿最美应用项目源码
源码下载: http://code.662p.com/view/12434.html 用到的第三方库 AFNetworking : OC的网络请求库 YYWebImageView : YY大神的图片加 ...
- 已上线运营的斗地主APP项目源码,出售(超低价)----(Android商业项目客户端)
已上线运营的斗地主APP商业项目源码,低价售于热爱学习的同行.您只需支付167元,即可拥有这套商业源码,提高您的技术开发能力.增色您的项目经历.增涨您的工资收入. 您可以运行此商业项目APK,尝试客户 ...
- 查看Hotspot源码,查看java各个版本源码的网站,如何查看jdk源码
java开发必知必会之看源码,而看源码的第一步则是找到源码
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- liunx上mysql源码安装mysql,搞定linux上MySQL编程(一):linux上源码安装MySQL
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 1. 首先下载源码包: ftp://ftp.jaist.ac.jp/pub/m ...
- java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实
好奇害死羊 很多小伙伴们做Java开发,天天写Java代码,肯定离不开Java基础环境:JDK,毕竟我们写好的Java代码也是跑在JVM虚拟机上. 一般来说,我们学Java之前,第一步就是安装JDK环 ...
- Go 源码里的这些 //go: 指令,go:linkname 你知道吗?
原文地址: Go 源码里的这些 //go: 指令,你知道吗? 一文解惑 //go:linkname 指令
最新文章
- 3dmax导出fbx时如何带贴图_houdini | 第一章 第三节 贴图与顶点动画
- SDN/NFV:现状,挑战和未来
- Apache2 httpd.conf 配置详解(一)
- 计算智能-群智能算法-粒子群算法matlab实现
- 王力宏《十八般武艺》全部歌曲试听
- OpenStack、Docker、KVM被评为最火的云开源项目
- Qt中查看ui_xxx.h文件方法
- 2018.7.28 二叉树的遍历规则(前序遍历、后序遍历、中序遍历)
- (C)libnet-发送arp/tcp/icmp数据包
- 北漂的程序员们,这寒冬你是否“有枝可依”?
- Define a New Server 没有tomcat选项
- PowerShell 远程连接与其它技巧
- Netty 源码分析之 零 磨刀不误砍柴工 源码分析环境搭建
- PAT甲之初窥门径(上)
- Latex在footnote或在bib中插入链接时出现波浪号~的解决方案
- 如何查看ps图片中边框的背景颜色
- 融合通讯四大关键词和三个应用场景
- java使用poi实现excel保护工作表实例代码(支持.xls和.xlsx)
- SET NOCOUNT { ON | OFF }
- win10 python3.8.10下ipython无响应处理
热门文章
- 第二期:如何通过知晓云快速生成分享海报
- arm spi 接口概述
- SPI接口的FPGA实现(一)——SPI接口的相关基础知识
- python-时间序列之ADF检验(1)
- web自动化如何在不同浏览器运行_2020自动化测试岗位需求的7项必备技能(更新版)...
- 2021世界移动通信大会 | 5G时代下,苏宁如何看待未来零售发展?
- Pytorch 常用 optimizer
- Flashlight App Android示例
- 流形学习(一)LLE 在 MATLAB 中的实现及实例
- Minecraft Forge API 类帮助文档(1.12.2)