我想为UITableView
构建一个图片下载器,以便可以将图像异步加载到表格视图中。
NSURLSession
自动使用NSURLCache
来缓存HTTP请求,我是否能够仅依赖于我的缓存机制?我注意到很多人使用NSCache
来保存下载的图像,但是如果NSURLSession
已经实现了缓存,那还有必要吗?如果有的话,不是很糟糕,因为我在内存中复制了一份图像副本吗?
此外,如果可能,我如何将其与UITableView
结合使用?细胞重用在分配背景转移的结果时提出了一个有趣的问题。
我不能简单地使用NSURLSessionDownloadTask
下载图像,并在完成块中设置单元格的图像,因为下载单元格时可能已经重复使用,从而导致它被设置为错误的单元格
let downloadTask = session.downloadTaskWithURL(URL, completionHandler: { location, response, error in
var dataFetchingError: NSError? = nil
let downloadedImage = UIImage(data: NSData.dataWithContentsOfURL(location, options: nil, error: &dataFetchingError))
dispatch_async(dispatch_get_main_queue()) {
// Might end up on the wrong cell
cell.thumbnail.image = downloadedImage
}
})
那么我如何确保指定的图像进入正确的单元格,并且我正确地缓存这些下载?
我知道SDWebImage等,但我想尝试在不使用库的情况下解决这个问题。
答案 0 :(得分:3)
您可以在最新调度到主队列之前查看在更新图像单元格之前单元格是否仍然可见。您可以使用cellForRowAtIndexPath
执行此操作(不要与类似名称的UITableViewDataSource
方法混淆):
dispatch_async(dispatch_get_main_queue()) {
if let updateCell = tableView.cellForRowAtIndexPath(indexPath) as? MyCell {
updateCell.thumbnail.image = downloadedImage
}
}
(注意,这假设在异步更新正在进行时,单元的索引路径不可能更改。如果在更新过程中可能插入/删除行,则不应该只使用旧的indexPath
,而是回到模型并重新计算此单元格的相应索引路径。)
显然,除了仅异步更新图像视图外,这些UIImageView
类别还提供其他优势。例如,如果重用该单元,则这些类别将取消该单元的任何旧的待处理网络请求。这一点非常重要,因此如果您快速滚动,当前可见的单元格将会在可能早已滚出视图的单元格的下载请求后面积压。
但如果您只关心在更新图像之前确保单元格仍然可见,则上述内容应解决此问题。
关于缓存问题,有两种不同类型的缓存可供讨论:RAM缓存和持久存储缓存。 SDWebImage和AFNetworking UIImageView
类别都实现了自己的RAM缓存(使用NSCache
)以提高性能。但是,对于持久性存储缓存,SDWebImage实现了自己的缓存,AFNetworking认为应该依赖内置的NSURLCache
。我同情SDWebImage透视图,因为(a)历史上NSURLConnection
缓存到iOS上的持久存储是不一致的; (b)服务器在响应中使用错误的标题很容易干扰缓存; (c)Apple在缓存某些内容以及何时没有缓存的标准方面一直很不透明。
所以,如果你想要丝滑平滑的滚动,那么实现NSCache
机制可能是一个很好的论据,但是你可以依靠NSURLCache
来实现持久存储缓存。