如何在 Swift 中使用 URLSession 缓存图像
Posted
技术标签:
【中文标题】如何在 Swift 中使用 URLSession 缓存图像【英文标题】:How to cache images using URLSession in Swift 【发布时间】:2017-04-13 21:50:01 【问题描述】:我想增强下面的代码以缓存图像,并且仅在之前未缓存图像时才下载它们。我似乎找不到任何关于如何使用 URLSession 对象来做到这一点的好例子。
extension UIImageView
func loadImageWithURL(_ url: URL) -> URLSessionDownloadTask
let session = URLSession.shared
let downloadTask = session.downloadTask(with: url, completionHandler: [weak self] url, response, error in
if error == nil, let url = url,
let data = try? Data(contentsOf: url), let image = UIImage(data: data)
DispatchQueue.main.async
if let strongSelf = self
strongSelf.image = image
)
downloadTask.resume()
return downloadTask
【问题讨论】:
nshipster.com/nsurlcache 不相关,你的if let strongSelf = self strongSelf.image = image
可以简化为self?.image = image
。
FWIW, NSURLCache
将根据服务器响应标头中提供的内容进行缓存。此外,它将根据记录不充分的规则限制缓存,最值得注意的是,如果下载量超过总缓存的 5%,它不会缓存它,无论服务器的标头说什么(这是增加缓存大小的原因之一如 Leo 提供的链接中所述)。
【参考方案1】:
为 Swift 4 更新
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>()
class ImageLoader: UIImageView
var imageURL: URL?
let activityIndicator = UIActivityIndicatorView()
func loadImageWithUrl(_ url: URL)
// setup activityIndicator...
activityIndicator.color = .darkGray
addSubview(activityIndicator)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
activityIndicator.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
imageURL = url
image = nil
activityIndicator.startAnimating()
// retrieves image if already available in cache
if let imageFromCache = imageCache.object(forKey: url as AnyObject) as? UIImage
self.image = imageFromCache
activityIndicator.stopAnimating()
return
// image does not available in cache.. so retrieving it from url...
URLSession.shared.dataTask(with: url, completionHandler: (data, response, error) in
if error != nil
print(error as Any)
DispatchQueue.main.async(execute:
self.activityIndicator.stopAnimating()
)
return
DispatchQueue.main.async(execute:
if let unwrappedData = data, let imageToCache = UIImage(data: unwrappedData)
if self.imageURL == url
self.image = imageToCache
imageCache.setObject(imageToCache, forKey: url as AnyObject)
self.activityIndicator.stopAnimating()
)
).resume()
用法:
// assign ImageLoader class to your imageView class
let yourImageView: ImageLoader =
let iv = ImageLoader()
iv.frame = CGRect(x: 10, y: 100, width: 300, height: 300)
iv.backgroundColor = UIColor(red: 0.94, green: 0.94, blue: 0.96, alpha: 1.0)
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
return iv
()
// unwrapped url safely...
if let strUrl = "https://picsum.photos/300/300".addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
let imgUrl = URL(string: strUrl)
yourImageView.loadImageWithUrl(imgUrl) // call this line for getting image to yourImageView
【讨论】:
【参考方案2】:对此的一个潜在解决方案是利用NSCache
来处理缓存。本质上,您要做的是检查您是否已经在本地加载图像,而不是每次在实际发出请求之前都获取。
然而,这是我的一个实现——它是一个子类而不是一个扩展:
class CustomImageView: UIImageView
// MARK: - Constants
let imageCache = NSCache<NSString, AnyObject>()
// MARK: - Properties
var imageURLString: String?
func downloadImageFrom(urlString: String, imageMode: UIViewContentMode)
guard let url = URL(string: urlString) else return
downloadImageFrom(url: url, imageMode: imageMode)
func downloadImageFrom(url: URL, imageMode: UIViewContentMode)
contentMode = imageMode
if let cachedImage = imageCache.object(forKey: url.absoluteString as NSString) as? UIImage
self.image = cachedImage
else
URLSession.shared.dataTask(with: url) data, response, error in
guard let data = data, error == nil else return
DispatchQueue.main.async
let imageToCache = UIImage(data: data)
self.imageCache.setObject(imageToCache!, forKey: url.absoluteString as NSString)
self.image = imageToCache
.resume()
此外,这里有一个有用的资源: https://www.hackingwithswift.com/example-code/system/how-to-cache-data-using-nscache
【讨论】:
【参考方案3】:URLSession DataTask 默认会自动缓存图片,只要服务端的缓存设置正常,你不需要在客户端做任何事情。图片是静态资源,不会在短时间内发生变化,因此服务器通常会将“Cache-Control”设置为“public, max-age:xxxxx”。 URLSession 默认缓存策略将图像缓存在内存和磁盘中。但是,它不会缓存大小超过为 URLCache 分配的磁盘大小的 5% 的图像,并且它也不会在后台线程中进行缓存。
【讨论】:
【参考方案4】:let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView
func loadImageFromUrl(urlString: String)
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage
self.image = imageFromCache
return
Alamofire.request(urlString, method: .get).response (responseData) in
if let data = responseData.data
DispatchQueue.main.async
if let imageToCache = UIImage(data: data)
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
self.image = imageToCache
【讨论】:
您好,欢迎来到 ***,请在回答之前正确阅读问题 - 问题中明确提到需要使用 URLSession 而不是任何第三方库来完成。以上是关于如何在 Swift 中使用 URLSession 缓存图像的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 URLSession 解析并将它们导入 Swift 的数组中?
Swift:如何在异步 urlsession 函数中返回一个值?
Swift:如何在异步 urlsession 函数中返回一个值?
如何在从 Swift 3 中的函数返回结果之前等待 URLSession 完成