【问题标题】:Is it possible to build an asynchronous image downloading mechanism for UITableViews with just NSURLSession? How?是否可以仅使用 NSURLSession 为 UITableViews 构建异步图像下载机制?如何?
【发布时间】:2014-12-07 13:53:33
【问题描述】:

我想为我的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 之类的,但我想尝试在不使用库的情况下解决这个问题。

【问题讨论】:

标签: ios cocoa-touch uitableview ios7 nsurlsession


【解决方案1】:

在更新图像单元之前,您可以将最终调度到主队列,看看该单元是否仍然可见。您可以使用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 的持久存储缓存。

【讨论】:

  • 该方法与数据源方法有何不同?
  • func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 是您在UITableViewDataSource 中实现的那个,它为新单元格实例化UITableViewCellUITableView 方法 func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell? 是您调用以返回“表示表格单元格的对象,如果单元格不可见或 indexPath 超出范围,则返回 nil。”
  • 明白了。 UITableView 那么是否只会将接下来可见的单元格出列? (它不会提前要求几个?)否则,如果这个方法只告诉我们它们是否可见,并且它们不可见,但是那个索引路径已经出队,我们就不会给它有机会设置它,对吧?
  • 不,试试看,你会发现它工作正常。顺便说一句,我真的很喜欢 AFNetworking 和 SDWebImage 的UIImageView 类别实现,所以即使您不使用这些类,也请看看它们的基本设计。它具有一定的优雅性(并且完全避免了这种理论上的担忧)。
  • this AFNetworking's?它在哪里执行此操作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多