这节我们来优化一下之前的 硬盘存储,看看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)
}
}