这节我们来优化一下之前的 硬盘存储,看看kingfiisher哪里做得好,我们稍微来学习一下。

从硬盘里检索图片模仿改进:


  open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? {

        let options = options ?? KingfisherEmptyOptionsInfo
        let computedKey = key.computedKey(with: options.processor.identifier)

        return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options)
    }


/*
        let options = options ?? KingfisherEmptyOptionsInfo
        let computedKey = key.computedKey(with: options.processor.identifier)
这两行不管是内存还是硬盘都要做一次,暂时不懂什么意思,先不管了。
*/

主要看:

  func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? {
        if let data = diskImageData(forComputedKey: key) {
            return serializer.image(with: data, options: options)
        } else {
            return nil
        }
    }

  func diskImageData(forComputedKey key: String) -> Data? {
        let filePath = cachePath(forComputedKey: key)
        return (try? Data(contentsOf: URL(fileURLWithPath: filePath)))
    }




 func cachePath(forComputedKey key: String) -> String {
// md5
        let fileName = cacheFileName(forComputedKey: key)
        return (diskCachePath as NSString).appendingPathComponent(fileName)
    }

func cacheFileName(forComputedKey key: String) -> String {
        return key.kf_MD5
    }

目前来看除了路径有点区别以外 大致都是差不多一样的,都是用 图片链接去做 md5运算来生成新的图片名字

重点来了,自己的进行改进:

import Foundation
import UIKit

//这里不写:继承类,它默认会继承什么呢? Object?
class ImageCache {
    //声明及初始化
    //可变字典做内存缓存 线程安全是个顾虑,虽然我没碰到过出什么问题,但是网上都这么说 - -.
    //    https://www.jianshu.com/p/239226822bc6
    //    var memoryDic:NSMutableDictionary? = [:] (旧)
    //Memory
    let memoryCache = NSCache<NSString,AnyObject>()
    //单例
    static let shared = ImageCache()
    //Disk
    fileprivate var fileManager: FileManager = FileManager.default
    //open 可以被任何人使用,包括override和继承。 现在的访问权限则依次为:open,public,internal,fileprivate,private。
    open var diskCachePath:String = ""

    //和 oc的sdwebimage差不多 需要在 init 设置初始路径. 暂时先不使用这个方法
    //    public init(name:String,path:String? = nil) {
    //        let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)"
    ////        memoryCache.name = cacheName
    //        let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
    //        diskCachePath = (dstPath as NSString).appendingPathComponent(cacheName)
    //
    //    }
    //    deinit {
    //        NotificationCenter.default.removeObserver(self)
    //    }

    //存储图片
    func storeImage(url:URL,image:UIImage?,toDisk:Bool?=nil) -> Void {

        let urlStr = "\(url)"
        //        memoryDic?.setValue(image, forKey:urlStr)
        memoryCache.setObject(image!, forKey: urlStr as NSString)
        if toDisk ?? false {
            //往硬盘里存 (旧)
            print("存硬盘")
            //先创建一个存储文件夹,后面这个创建代码写到该类初始化的时候.注意路径写的时候斜杠
            //                        let myDirectory:String = NSHomeDirectory() + "/Documents/ImageCache/"
            //                        let fileManager = FileManager.default
            //                        //withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建
            //                        try! fileManager.createDirectory(atPath: myDirectory,                                     withIntermediateDirectories: true, attributes: nil)
            //                        //这里路径也做一下处理,文件名字做一下md5防止重复,这里md5路径写法有点小问题,到时候参考Kingfisher的算法吧,不过md5本身算法没问题,只是我用url字符串去做md5不知道妥当不妥当。
            //                        let md5FileNamePath = myDirectory + urlStr.md5()
            //                        fileManager.createFile(atPath: md5FileNamePath, contents: UIImageJPEGRepresentation(image!, 1.0), attributes: nil)
            //                        print("路径:\(md5FileNamePath)")
            //暂时没有搞懂kingfisher的路径,我们先按之前的,稍微改进了下
            let myDirectory:String = NSHomeDirectory() + "/Documents/ImageCache/"
            diskCachePath = myDirectory
            //            self.fileManager = FileManager.default
            try! self.fileManager.createDirectory(atPath: myDirectory,                                     withIntermediateDirectories: true, attributes: nil)
            self.fileManager.createFile(atPath: self.cachePath(forKey: urlStr), contents: UIImageJPEGRepresentation(image!, 1.0), attributes: nil)
            print("最后的路径:\(self.cachePath(forKey: urlStr))")
        }
    }

    func cachePath(forKey key:String) ->String {
        let fileName = cacheFileName(forKey: key)
        return (diskCachePath as NSString).appendingPathComponent(fileName)
    }

    //在内存里检索图片
    func retrieveImageInMemoryCache(forKey key:String) -> UIImage? {
        //如果内存里有 直接到内存里拿去显示
        let urlStr = "\(key)"
        if let image =  memoryCache.object(forKey: urlStr as NSString) {
            return image as? UIImage
        }

        return nil
    }
    //在硬盘里检索图片
    func retrieveImageInDiskCache(forKey key:String) -> UIImage? {

        //如果内存里没有 再去硬盘里找
        let urlStr = "\(key)"
        let myDirectory:String = NSHomeDirectory() + "/Documents/ImageCache/"
        //        let md5FileNamePath = myDirectory + urlStr.md5() (旧)
        diskCachePath = myDirectory
        let img = UIImage.init(contentsOfFile: self.cachePath(forKey: urlStr))
        if (img != nil) {
            return img
        }
        return nil
    }

}

//不知道为什么kingfisher作者用了大量的 extension
extension ImageCache {

    func cacheFileName(forKey key:String) -> String {
        return key.md5()
    }

}

import Foundation
import UIKit
/// typealias用来为已存在的类型重新定义名称的。
typealias ImageView = UIImageView
extension ImageView {
    func sfsc_setImage(url:URL?) -> Void {
        guard let url = url else {
            return
        }
        //guard let保证代码执行至此 url 一定有值!!
        tryToRetrieveImageFromCache(with: url)
    }

    func tryToRetrieveImageFromCache(with url:URL) {
        let urlStr = "\(url)"
        //1.创建缓存单例类
        let targetCache = ImageCache.shared;
        //2.用缓存单例类去调用自己的取图片的方法(内存检索图片)
        if let image = targetCache.retrieveImageInMemoryCache(forKey: urlStr) {
            self.image = image
            return
        } else {
            //从硬盘缓存检索图片
            if let image = targetCache.retrieveImageInDiskCache(forKey: urlStr){
                self.image = image
                return
            }
        }

        //加载图片设置成多线程队列任务来完成,每一张图片加载是一个任务
        //创建队列
        let queue = OperationQueue()
        //设置最大并发数
        queue.maxConcurrentOperationCount = 2
        //创建operation
        let loadOperation = LoadOperation()
        //初始化方法,配置操作任务里需要的参数。感觉这样写法有点奇怪
        loadOperation.initWithURL(anUrl: url, delegate: self)
        //将任务添加到队列
        queue.addOperation(loadOperation)
    }

}

11-14 17:51