Swift simplified sdwebimage (making wheels)

Mr. ran 2021-09-15 08:26:26

The implemented

  • Network request to get pictures
  • Memory cache pictures
  • Disk cache picture
  • Get disk cache data size
  • Delete the cache of the specified picture
  • Delete all picture caches
  • UIImageView Corresponding classification ( Support the change of occupation bitmap and occupation bitmap display mode )

For other classes to set up network pictures, please refer to UIImageView classification


extension <# Class name #>: RBWebImageProtocol {
func <# Method name #>(url: URL, <# The other parameters #>) {
// 1. Get the picture from the cache and load 
if let cacheImage = RBWebImageCach.getCacheImage(urlStr: url.absoluteString) {
            // Picture found in cache , Update picture 
            <#code#>
            return
        }
// 2. When there is no picture in the cache, request picture data through the network 
getData(url: url!) {[weak self] (data) in
            if data == nil {
// The data requested by the network is empty 
<#code#>
            }else {
                if let ima = UIImage(data: data!) {
                    // Network request to picture 
<#code#>
                    // Cache pictures data
                    RBWebImageCach.saveImageData(data: data!, urlStr: (url!.absoluteString))
                }else {
                    // Failed to convert the data requested by the network to pictures 
<#code#>
                }
            }
        }
}
}
 Copy code 

technological process

  1. adopt url Set pictures
  2. adopt url Get pictures from cache ( Now look in memory , Not found in memory, and then go to disk to find )
  3. After the cache finds the picture, it will be set directly
  4. If it is not found in the cache, it requests the picture through the network
  5. After requesting data, go to the main thread to set the picture

Main documents

RBWebImageProtocol

agreement , Through the extension of the protocol, the function of obtaining picture data on the network is realized

import Foundation
import UIKit
protocol RBWebImageProtocol {
/// adopt url obtain data
/// - Parameters:
/// - url: url
/// - completion: Callback after completion 
func getData(url: URL, completion: ((Data?) -> Void)?)
}
extension RBWebImageProtocol {
/// adopt url obtain data
/// - Parameters:
/// - url: url
/// - completion: Callback after completion 
func getData(url: URL, completion: ((Data?) -> Void)? = nil) {
// Create parallel queues to execute asynchronously 
DispatchQueue(label: "com.RBWebImage.concurrentQueue",attributes:.concurrent).async {
do {
let data: Data = try Data(contentsOf: url)
if data.count > 0 {
completion?(data)
}else {
completion?(nil)
}
} catch _ {
completion?(nil)
}
}
}
}
 Copy code 

RBWebImageCach

Cache management class , All functions related to caching

  • RBWebImageMemoryManager Memory cache management class
  • RBWebImageDiskManager Disk cache management class
import Foundation
import UIKit
fileprivate func showLog(_ msg: String) {
if isShowRBWebImageLog {
print(msg)
}
}
class RBWebImageCach {
/// Get images from the cache 
/// - Parameter urlStr: picture url
/// - Returns: Pictures in cache 
class func getCacheImage(urlStr: String) -> UIImage? {
let key = getKeyString(urlStr: urlStr)
// Get pictures from memory 
showLog(" Will go to memory to get the cache :\(urlStr)")
if let cacheImage = RBWebImageMemoryManager.shared.getImage(key: key) {
showLog(" Picture found in memory :\(urlStr)")
return cacheImage
}
showLog(" Not found in memory , Will go to disk to get cache :\(urlStr)")
// When there is no memory , Get picture data from disk 
if let data = RBWebImageDiskManager.shared.getData(key: key) {
if let cacheImage = UIImage(data: data) {
showLog(" Picture found on disk :\(urlStr)")
return cacheImage
}
}
return nil
}
/// Save picture data 
/// - Parameters:
/// - data: Picture data 
/// - urlStr: picture url
class func saveImageData(data: Data, urlStr: String) {
let key = getKeyString(urlStr: urlStr)
if let image = UIImage(data: data) {
// Cache into memory 
showLog(" Store in memory :\(urlStr)")
RBWebImageMemoryManager.shared.saveImage(image: image, key: key)
// Cache to disk 
showLog(" Storage to disk :\(urlStr)")
RBWebImageDiskManager.shared.saveImage(data: data, key: key)
}else {
showLog(" Invalid data :\(urlStr)")
}
}
/// Delete all cached data 
class func deleteAllCacheData() {
// Delete all cached data in memory 
RBWebImageMemoryManager.shared.deleteAllImage()
// Delete all cached data on disk 
RBWebImageDiskManager.shared.deleteAllCacheData()
}
class func deleteCacheData(urlStr: String) {
let key = getKeyString(urlStr: urlStr)
// Delete cached data in memory 
RBWebImageMemoryManager.shared.deleteImage(key: key)
// Delete cached data on disk 
RBWebImageDiskManager.shared.deleteCacheData(key: key)
}
/// Get the size of cached picture data 
/// - Returns: Size of picture data 
class func getCacheSize() -> Double {
return RBWebImageDiskManager.shared.getCacheSize()
}
/// according to url String to get the cache key
/// - Parameter urlStr: url character string 
/// - Returns: key
private class func getKeyString(urlStr: String) -> String {
var key = urlStr
for s in ":/\\." {
key = key.replacingOccurrences(of: String(s), with: "_")
}
return key
}
}
/// Memory cache management class 
class RBWebImageMemoryManager {
static let shared = RBWebImageMemoryManager()
private init() {}
private let cache = NSCache<NSString, UIImage>()
private var keys = Array<NSString>()
/// Save picture to memory 
/// - Parameters:
/// - image: picture 
/// - key: key
func saveImage(image: UIImage, key: String) {
let nKey = key as NSString
if !keys.contains(nKey) {
keys.append(nKey)
}
cache.setObject(image, forKey: nKey)
}
/// adopt key Get pictures in memory 
/// - Parameter key: key
/// - Returns: Pictures in memory 
func getImage(key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
/// Delete all pictures in memory 
func deleteAllImage() {
cache.removeAllObjects()
keys.removeAll()
}
/// Delete key The corresponding picture 
/// - Parameter key: key
func deleteImage(key: String) {
let nKey = key as NSString
cache.removeObject(forKey: nKey)
}
}
/// Disk cache management class 
class RBWebImageDiskManager {
static let shared = RBWebImageDiskManager()
private init() {}
private let cachePath = "\(NSHomeDirectory())/Library/Caches/BGWebImageCache"// Cache path 
/// Save data to disk 
/// - Parameters:
/// - data: data 
/// - key: key
func saveImage(data: Data, key: String) {
let path: String = getFullCachePath(key: key)
if FileManager.default.createFile(atPath: path, contents: data, attributes: nil) {
showLog(" Saved successfully ")
}else {
showLog(" Save failed ")
}
}
/// adopt key Get picture data on disk 
/// - Parameter key: key
/// - Returns: Picture data on disk 
func getData(key: String) -> Data? {
var data:Data?
let path: String = getFullCachePath(key: key)
if FileManager.default.fileExists(atPath: path) {
data = FileManager.default.contents(atPath: path)
}
return data
}
/// Delete all picture data on disk 
func deleteAllCacheData() {
let fileManager: FileManager = FileManager.default
if fileManager.fileExists(atPath: cachePath) {
do {
try fileManager.removeItem(atPath: cachePath)
} catch {
showLog(" Delete disk cache exception ")
}
}
}
/// Delete key Corresponding picture data 
/// - Parameter key: key
func deleteCacheData(key: String) {
let path = getFullCachePath(key: key)
let fileManager: FileManager = FileManager.default
if fileManager.fileExists(atPath: path) {
do {
try fileManager.removeItem(atPath: path)
} catch {
showLog(" Delete disk cache exception ")
}
}
}
/// Get the total size of pictures on disk 
/// - Returns: size
func getCacheSize() -> Double {
let manage = FileManager.default
if !manage.fileExists(atPath: cachePath) {
return 0
}
let childFilePath = manage.subpaths(atPath: cachePath)
var size:Double = 0
for path in childFilePath! {
let fileAbsoluePath = cachePath+"/"+path
size += getFileSize(filePath: fileAbsoluePath)
}
return size
}
/// adopt key Get the full cache path 
/// - Parameter key: key
/// - Returns: Full cache path 
private func getFullCachePath(key: String) -> String {
let path = "\(cachePath)/\(key)"
let fileManager: FileManager = FileManager.default
if !(fileManager.fileExists(atPath: cachePath)) {
do {
try fileManager.createDirectory(atPath: cachePath, withIntermediateDirectories: true, attributes: nil)
return path
}catch {
showLog(" Cache full path setting failed ")
return ""
}
}
return path
}
/// Get file size 
/// - Parameter filePath: File path 
/// - Returns: file size 
private func getFileSize(filePath: String) -> Double {
let manager = FileManager.default
var fileSize:Double = 0
do {
let attr = try manager.attributesOfItem(atPath: filePath)
// fileSize = Double(attr[FileAttributeKey.size] as! UInt64)
let dict = attr as NSDictionary
fileSize = Double(dict.fileSize())
} catch {
showLog("\(error)")
}
return fileSize
}
}
 Copy code 

UIImageView+RBWebImage

UIImageView An extension of , Realization UIimageView Set the function of network picture

import UIKit
let isShowRBWebImageLog = true
extension UIImageView: RBWebImageProtocol {
/// Set up network pictures 
/// - Parameters:
/// - url: url
/// - defaultImage: Occupation map 
/// - isKeepOriginal: When there were pictures , Whether to keep the original picture (true: Do not replace bitmap ,false: Replace the bitmap every time )
/// - completion: Callback after completion 
func rbSetImage(url: URL?, defaultImage: UIImage?, isKeepOriginal: Bool = false, completion: ((UIImage?) -> Void)? = nil) {
showLog(" Start setting up network pictures :\(url?.absoluteString ?? "")")
if !isKeepOriginal {
self.image = nil
}
let originImage = self.image
if originImage == nil && defaultImage != nil {
self.image = defaultImage!
}
if url == nil {
completion?(nil)
showLog(" Setup failed :url empty ")
return
}
// Get the picture from the cache and load 
if let cacheImage = RBWebImageCach.getCacheImage(urlStr: url!.absoluteString) {
// Update the picture in the main thread 
DispatchQueue.main.async {[weak self] in
self?.image = cacheImage
}
self.showLog(" Set up the success :\(url!.absoluteString)")
return
}
showLog(" Failed to get picture from cache :\(url!.absoluteString)")
// When there is no picture in the cache, request picture data through the network 
showLog(" Start requesting picture data through the network :\(url!.absoluteString)")
getData(url: url!) {[weak self] (data) in
if data == nil {
completion?(nil)
self?.showLog(" Setup failed : The data requested by the network is empty -\(url!.absoluteString)")
}else {
if let ima = UIImage(data: data!) {
// Update the picture in the main thread 
DispatchQueue.main.async {[weak self] in
self?.image = ima
}
// Cache pictures data
RBWebImageCach.saveImageData(data: data!, urlStr: (url!.absoluteString))
self?.showLog(" Set up the success :\(url!.absoluteString)")
completion?(ima)
}else {
self?.showLog(" Setup failed : Failed to convert the data requested by the network to pictures -\(url!.absoluteString)")
completion?(nil)
}
}
}
}
/// Set up network pictures 
/// - Parameters:
/// - urlStr: url character string 
/// - defaultImageName: Bitmap name 
func rbSetImage(urlStr: String, defaultImageName: String?) {
var defauleImage: UIImage? = nil
if defaultImageName != nil {
defauleImage = UIImage(named: defaultImageName!)
}
var url: URL? = nil
if let us = urlStr.urlString() {
url = URL(string: us)
}
rbSetImage(url: url, defaultImage: defauleImage, isKeepOriginal: false, completion: nil)
}
/// Print log 
/// - Parameter msg: msg Log information 
private func showLog(_ msg: String) {
if isShowRBWebImageLog {
print(msg)
}
}
}
extension String {
/// format url
/// - Returns: format url character string 
func urlString() -> String? {
if self.count == 0 {
return nil
}
let urlStr = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, self as CFString, "!$&'()*+,-./:;[email protected]_~%#[]" as CFString) as String?
if urlStr == nil || urlStr?.count == 0 {
return nil
}
return urlStr
}
}
 Copy code 
Please bring the original link to reprint ,thank
Similar articles

2021-09-15