Kingfisher 源码阅读以及部分小收获

作者: DingGa | 来源:发表于2021-02-24 12:51 被阅读0次
    screenshot.png

    Kingfisher

    部分收获:
    1. Kingfisher的 kf 写法
      同样是链式编程, 在SnapKit中,view.snp是通过对View进行扩展实现的
      类似snp的写法:
    public var snp: ConstraintViewDSL {
            return ConstraintViewDSL(view: self)
        }
    

    这种写法来为类添加一个不存在的属性, snp
    但是在 Kingfisher中,onev 是使用了泛型结合协议来做这个事情的.

    /**
     A type that has Kingfisher extensions.
     */
    public protocol KingfisherCompatible {
        associatedtype CompatibleType
        var kf: CompatibleType { get }
    }
    
    public extension KingfisherCompatible {
        public var kf: Kingfisher<Self> {
            return Kingfisher(self)
        }
    }
    
    extension ImageView: KingfisherCompatible { }
    extension Image: KingfisherCompatible { }
    

    给ImageView和image遵守了一个协议 KingfisherCompatible
    从而实现了

     imageView.kf 就等同于 let kf: Kingfisher = Kingfisher<imageView>
    
    1. 读写权限不同
      fileprivate(set)
    /// It will be `nil` if `indicatorType` is `.none`.
        public fileprivate(set) var indicator: Indicator? {
            get {
                ...
            }
            
            set {
                ...
            }
        }
    
    1. Swift5 支持的Result
    func cacheImage(_ result: Result<ImageLoadingResult, KingfisherError>)
            {
                switch result {
                case .success(let value):
                   handle(value)
                case .failure(let error):
                   handle(error)
                }
            }
    

    value是ImageLoadingResult, error是KingfisherError

    1. 禁止网络请求的缓存
      4.1. cachePolicy
    URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
    

    4.2 URLSessionConfiguration.ephemeral
    为了防止网络请求在我们不知道的情况下被缓存,可以使用ephmeral来进行请求

    URLSession是一个可以响应发送或者接受HTTP请求的关键类,可以通过URLSessionConfiguration类新建URLSession实例。有以下三种方式:

    URLSessionConfiguration.default
    默认configuration实例创建方式,使用硬盘上持久化全局缓存、证书(credential)和cookie的存储对象
    URLSessionConfiguration.ephemeral
    唯一跟默认configuration不一样的是所以与会话(session)相关的数据都存储在内存中
    URLSessionConfiguration.background(withIdentifier: "ConfigurationID")
    让会话在后台执行上载或下载任务。即使应用程序本身被暂停或终止,传输仍将继续

    来说说Kingfisher简单使用

    let url = URL(string: imageURL)
    imageView.kf.setImage(with: url)
    

    kf 定义
    先看 kf 的定义,返回一个包含自己的 KingfisherWrapper 对象,可以调用 Setting Image 一系列函数。

    extension ImageView: KingfisherCompatible { }
    
    extension KingfisherCompatible {
        /// Gets a namespace holder for Kingfisher compatible types.
        public var kf: KingfisherWrapper<Self> {
            get { return KingfisherWrapper(self) }
            set { }
        }
    }
    
    public struct KingfisherWrapper<Base> {
        public let base: Base
        public init(_ base: Base) {
            self.base = base
        }
    }
    

    设置方法

    @discardableResult
    public func setImage(
        with resource: Resource?,
        placeholder: Placeholder? = nil,
        options: KingfisherOptionsInfo? = nil,
        progressBlock: DownloadProgressBlock? = nil,
        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
    {
        return setImage(
            with: resource.map { .network($0) },
            placeholder: placeholder,
            options: options,
            progressBlock: progressBlock,
            completionHandler: completionHandler)
    }
    

    @discardableResult:表示取消不使用返回值的警告

    可以看到 setImage(with: url) 内部调用了另外一个相似函数,只不过 source 参数类型从 Resource? 变成 Source? ,我们来看下这两者的区别:

    public protocol Resource {
        var cacheKey: String { get }
        var downloadURL: URL { get }
    }
    
    public enum Source {
        public enum Identifier {
            public typealias Value = UInt
            static var current: Value = 0
            static func next() -> Value {
                current += 1
                return current
            }
        }
    
        case network(Resource)
        case provider(ImageDataProvider)
    
        public var cacheKey: String {
            switch self {
            case .network(let resource): return resource.cacheKey
            case .provider(let provider): return provider.cacheKey
            }
        }
      
        public var url: URL? {
            switch self {
            case .network(let resource): return resource.downloadURL
            case .provider(_): return nil
            }
        }
    }
    

    Resource 是协议,Resource 标志这图片来自网络,提供cacheKey : String,downloadURL:URL。URL实现该协议,将absoluteString作为缓存的Key。

    Source 是枚举,Source 有两种类型:.network(Resource) 和 .provider(ImageDataProvider)

    imageView.kf.setImage(with: url) 可以直接传入 url,是因为 URL 实现了 Resource 协议

    extension URL: Resource {
        public var cacheKey: String { return absoluteString }
        public var downloadURL: URL { return self }
    }
    

    ImageDataProvider
    ImageDataProvider是一个协议,标志这图片来自网络,提供cacheKey : String,func data() 来缓存与生成Image。
    KingFisher提供了三种默认的Provider:

    LocalFileImageDataProvider, 从本地file中读取Image
    Base64ImageDataProvider,从Base64中读取Image
    RawImageDataProvider, 从Data中读取Image
    

    Placeholder
    Placeholder 也是一个协议,提供在ImageView上添加自身和移除自身的功能函数:

        func add(to imageView: ImageView)
        func remove(from imageView: ImageView)
    

    Image和View有默认的配置:

    extension Placeholder where Self: Image 
    extension Placeholder where Self: View 
    

    Image的默认方法是设置imageView的image为自身
    View的默认方法是添加覆盖imageView的子View

    KingfisherOptionsInfo

     public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
        
        /**
    Items could be added into KingfisherOptionsInfo.
    */
    public enum KingfisherOptionsInfoItem {
        case targetCache(ImageCache) //设置缓存器,Kingfisher用这个缓存器来缓存展示的图片
        case originalCache(ImageCache) //设置缓存器,Kingfisher用这个缓存器来缓存下载的原始图片
        case downloader(ImageDownloader) //设置下载器,Kingfisher用这个下载器来下载数据
        case transition(ImageTransition) //设置下载完成之后的动画
        case downloadPriority(Float) //0.0~1.0 设置下载优先级
        case forceRefresh //忽视缓存
        case fromMemoryCacheOrRefresh //先尝试从内存缓存读取,如果没有,则重新下载,不会读取磁盘缓存
        case forceTransition //从缓存读取的图片也会进行动画处理
        case cacheMemoryOnly //只通过内存缓存图片
        case waitForCache //缓存完成之后才调用completion block
        case onlyFromCache  //只通过缓存读取图片,不会下载
        case backgroundDecode //使用图片前线在后台线程上解码
        case callbackDispatchQueue(DispatchQueue?) //设置回调在那个队列上
        case scaleFactor(CGFloat) // data转成Image时的Scale
       .....
    }
    

    KingfisherManager
    KingfisherManager主要由两部分组成,ImageDownloader用于管理下载;ImageCache用于管理缓存。其主要函数即为:

    func retrieveImage(
            with source: Source,
            options: KingfisherParsedOptionsInfo,
            completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask?
    

    阅读源码之后,我们可以发现函数loadAndCacheImage负责下载及缓存图片,函数retrieveImageFromCache负责读出cache中的图片。

    图片下载

    ImageDownloader
    下载图片的代码如下:

    let downloader = options.downloader ?? ImageDownloader.default
    guard let task = downloader.downloadImage(
        with: resource.downloadURL,
        options: options,
        completionHandler: cacheImage) else {
      return nil
    }
    return .download(task)
    

    ImageDownloader

    网络请求的抽象:
    
    private let sessionDelegate: SessionDelegate
    private var session: URLSession
    open var sessionConfiguration = URLSessionConfiguration.ephemeral {
      didSet {
        session.invalidateAndCancel()
        session = URLSession(configuration: sessionConfiguration, delegate: sessionDelegate, delegateQueue: nil)
      }
    }
    
    // 自定义证书的验证逻辑
    // https://www.cnblogs.com/Code-life/p/7806824.html
    open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable?
    

    这里放一个Kingfisher读取图片缓存的逻辑图.


    image.png

    相关文章

      网友评论

        本文标题:Kingfisher 源码阅读以及部分小收获

        本文链接:https://www.haomeiwen.com/subject/yaghfltx.html