以前閱讀的OC版本的SDWebimage,因爲Swift發展趨勢已經成熟,也是時候練習練習了(想起了劉德華的練習),邊思考邊閱讀邊寫吧,翱翔在這個世界裏。git
一開始我打算從最初的開始閱讀的,可是限於Kingfisher版本跟隨Swift 版本一塊兒升級,因此若是想閱讀最初的就要下載Xcode 8 甚至更前面的Xcode版本。 2.0到3.0的語法變化比較大,因此我直接從3.0開始讀了,下載的是Xcode 9版本,最低能夠設置 3.2版本,可是沒有關係,3.0之後 API變更就小了。下載最低版本請自行搜索。github
進入正題:web
3.0最第一版本源碼地址:https://github.com/onevcat/Kingfisher/tree/3.0.0-beta.1swift
先來思考一下用Swift語法來寫一個 控件加載網絡圖片顯示:緩存
先用 Main.storyboard 建立一個 CollectionView控件 上面放一張圖片在cell裏,而後多返回幾個cell ,而後建立一個 相似oc的分類,把url轉換到image經常使用的步驟寫到分類裏,每次用到該類的控件加載網絡圖片就能夠一個方法搞定 不用那麼複雜。最簡單的實現:網絡
import UIKit class ViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } //這裏collectionView的代理方法我看有人寫個擴展,爲何呢?寫本類不行麼? override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 10 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell:CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell // 初始化url圖片 let url:URL = URL.init(string:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1572933973277&di=dfcdb7e4aa6339a22ca5e40270987790&imgtype=0&src=http%3A%2F%2Fpic44.nipic.com%2F20140723%2F18505720_094503373000_2.jpg")! cell.cellImageView.sfsc_setImage(url: url) return cell } } import UIKit class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var cellImageView: UIImageView! } ///如下是圖片加載代碼 import Foundation import UIKit /// typealias用來爲已存在的類型從新定義名稱的。 typealias ImageView = UIImageView extension ImageView { func sfsc_setImage(url:URL) -> Void { //------- 這部分代碼,每一個須要網絡圖片的都會用到,因此提取出來 --------- //轉爲data類型 let data : NSData! = NSData(contentsOf: url) //判斷data不爲空,這裏是由於swift對類型要求很嚴,若是未空的話,會崩潰 if data != nil { //賦值圖片 self.image = UIImage.init(data: data as Data, scale: 1) }else{ // 不然就賦值默認圖片 self.image = UIImage.init(named: "005") } } }
一個簡單的圖片加載就實現了,接下來咱們來看看Kingfisher的做者是怎麼實現的吧:閉包
// MARK: - Set Images /** * Set image to use from web. */ extension ImageView { /** Set an image with a resource, a placeholder image, options, progress handler and completion handler. - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - returns: A task represents the retrieving process. - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. //下面是翻譯註釋: / * * 使用資源、佔位符圖像、選項、進度處理程序和完成處理程序設置圖像。 -參數資源:資源對象包含' cacheKey '和' downloadURL '等信息。 -參數佔位符:一個佔位符圖像檢索圖像在URL。 -參數選項:一個字典能夠控制一些行爲。更多信息請參見「翠鳥選擇sinfo」。 -參數progressBlock:當圖像下載進度更新時調用。 -參數completionHandler:調用時的圖像檢索和設置。 -返回:任務表示檢索過程。 -注意:' progressBlock '和' completionHandler '將在主線程中被調用。 ' optionsInfo '中指定的' CallbackDispatchQueue '將不會在此方法的回調中使用。 * / */ /* swift正常的方法若是有返回值的話,調用的時候必須有一個接收方,不然的話編譯器會報一個警告,若是在方法前加上 @discardableResult 不處理的時候就不會 有警告了。也能夠用一個通配符接收方法返回值,能夠達到一樣的目的。 */ @discardableResult public func kf_setImage(with resource: Resource?, placeholder: Image? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { image = placeholder guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty } let showIndicatorWhenLoading = kf_showIndicatorWhenLoading var indicator: IndicatorView? = nil if showIndicatorWhenLoading { indicator = kf_indicator indicator?.isHidden = false indicator?.kf_startAnimating() } kf_setWebURL(resource.downloadURL) var options = options ?? KingfisherEmptyOptionsInfo if shouldPreloadAllGIF() { options.append(.preloadAllGIFData) } let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, progressBlock: { receivedSize, totalSize in if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, completionHandler: {[weak self] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { guard let sSelf = self, imageURL == sSelf.kf_webURL else { return } sSelf.kf_setImageTask(nil) guard let image = image else { indicator?.kf_stopAnimating() completionHandler?(nil, error, cacheType, imageURL) return } guard let transitionItem = options.kf_firstMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else { indicator?.kf_stopAnimating() sSelf.image = image completionHandler?(image, error, cacheType, imageURL) return } #if !os(macOS) UIView.transition(with: sSelf, duration: 0.0, options: [], animations: { indicator?.kf_stopAnimating() }, completion: { _ in UIView.transition(with: sSelf, duration: transition.duration, options: [transition.animationOptions, .allowUserInteraction], animations: { // Set image property in the animation. transition.animations?(sSelf, image) }, completion: { finished in transition.completion?(finished) completionHandler?(image, error, cacheType, imageURL) } ) }) #endif } }) kf_setImageTask(task) return task } }
能夠看出代碼處理各方面仍是比較全面的,不僅僅是咱們這樣僅僅顯示加載就能夠,咱們先從簡單一點點來,先忽略掉 佔位圖 和 加載進度還有 閉包這些,單從簡單的處理和緩存看起,這樣能夠和OC以前的最基礎的SDWebImage作個對照:app
先從上面的代碼學一個簡單的判斷:ide
// guard let語法:guard let 判斷以後 守護 必定有值 若是沒有值 在guard let 的{} 裏 直接返回。 guard let resource = resource else { completionHandler?(nil, nil, .none, nil) return .empty }
完善本身的方法:this
func sfsc_setImage(url:URL) -> Void { //------- 這部分代碼,每一個須要網絡圖片的都會用到,因此提取出來 --------- /* guard let語法:guard let 判斷以後 守護 必定有值 若是沒有值 在guard let 的{} 裏 直接返回。 若是須要守護多個值直接在後面跟 逗號 分割開來。 */ guard let url = url else { return } //代碼執行至此 url 必定有值!! //轉爲data類型 let data : NSData! = NSData(contentsOf: url) //判斷data不爲空,這裏是由於swift對類型要求很嚴,若是未空的話,會崩潰 if data != nil { //賦值圖片 self.image = UIImage.init(data: data as Data, scale: 1) }else{ // 不然就賦值默認圖片 self.image = UIImage.init(named: "005") } }
public func kf_setImage ... 這裏的public 暫時先不加 看後面會有什麼影響,若是我本身寫 我就不會加。
import Foundation /// `Resource` protocol defines how to download and cache a resource from network. public protocol Resource { /// The key used in cache. var cacheKey: String { get } /// The target image URL. var downloadURL: URL { get } } /** ImageResource is a simple combination of `downloadURL` and `cacheKey`. When passed to image view set methods, Kingfisher will try to download the target image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. */ public struct ImageResource: Resource { /// The key used in cache. public let cacheKey: String /// The target image URL. public let downloadURL: URL /** Create a resource. - parameter downloadURL: The target image URL. - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. - returns: A resource. */ public init(downloadURL: URL, cacheKey: String? = nil) { self.downloadURL = downloadURL self.cacheKey = cacheKey ?? downloadURL.absoluteString } } /** URL conforms to `Resource` in Kingfisher. The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. If you need customize the url and/or cache key, use `ImageResource` instead. */ extension URL: Resource { public var cacheKey: String { return absoluteString } public var downloadURL: URL { return self } }
思考這裏爲何用協議?