哪种方式对从集合视图中的 URL 下载图像的性能更好



哪种方式对从 CollectionView 内的 URL 下载图像的性能更好?

例如 从服务器 url 下载图像,可以通过多种方式完成,哪种方式更好: 1) 使用 QoS = 实用程序创建全局调度并发队列。然后只需执行异步下载代码。

private func donwnloadwithGlobalQueue(at indexpath : IndexPath){
DispatchQueue.global(qos: .utility).async { [weak self] in
guard let weakself = self else{
return
}
let url = weakself.urls[indexpath.item]
guard let data = try? Data(contentsOf: url),
let image = UIImage(data: data) else{
return
}
DispatchQueue.main.async {
if let cell = self?.collectionView.cellForItem(at: indexpath) as? PhotoCell {
cell.display(image: image)
}
}
}
}

2)使用NSURLSSION数据任务方法。

private func downloadwithURLSession(at indexpath : IndexPath){
URLSession.shared.dataTask(with: urls[indexpath.row]) { [weak self]  (data, response, error) in
guard let weakself = self,
let data = data,
let image = UIImage(data: data) else{
return
}
DispatchQueue.main.async {
if let cell = weakself.collectionView.cellForItem(at: indexpath) as? PhotoCell {
cell.display(image: image)
}
}
}.resume()
}

下载性能将无法区分。

但是,您应该使用URLSession(或像翠鸟这样的库,它也使用URLSession),因为 (a) 它可以让您更好地控制连接;(b) 如果失败,它会为您提供更多诊断信息;(c) 它提供取消功能。我们经常谈论不同方法的"优缺点",但在Data(contentsOf:)的情况下,这只是"缺点"。

话虽如此,我们有五种关键方法可以提高性能:

  1. 缓存

    确保在应用下载图像后,如果该单元格滚动到视图之外,然后又重新进入视图,则不会启动新的网络请求。您刚刚下载了资产,因此您应该使用它。有时URLSession免费提供这种缓存,尽管这有点取决于您的 Web 服务器的配置方式。所以我们经常做我们自己的缓存,既是 RAM 中的小缓存(例如NSCache)和持久存储中的更大缓存(例如.cachesDirectory)。像翠鸟这样的库会为你完成所有这些缓存。

    这不会改善初始提取,但通过避免重新获取已下载的内容,它大大提高了性能。
     

  2. 预取

    您可以启用预取,在预取中,当它为可见单元格请求数据时,它将请求可能很快滚动到视图中的单元格的数据。这意味着,假设它有机会赶上,当您滚动时,它将显示已经预取数据的单元格(当这种情况发生时,它将为接下来可能出现的单元格预取数据)。

    因此,同样,这不会改善初始提取。但是,如果它有机会赶上,当你以适度的速度滚动浏览时,感觉会快得多。

    请参阅isPrefetchingEnabledUICollectionView文档中的"预取集合视图单元格和数据"讨论。
     

  3. 取消

    快速滚动浏览集合视图时,您需要确保取消对不再可见的单元格的请求。 例如,如果您快速滚动以便查看单元格 100-119,您真的不希望积压等待不再可见的单元格 0-99 的图像。

    这就是您真正想要使用基于URLSession的方法的原因,您可以在其中保存对正在运行URLSessionTask的弱引用,并在重用单元格时取消它。或者如果使用翠鸟,我们会cancelDownloadTaskUIImageView

  4. 下载适当大小的资源

    如果可能,请下载适当大小的资产。例如,假设图像为 2,000 × 2,000 像素。但是,假设您使用的是3×视网膜设备,其图像视图为100×100点。这意味着您应该下载 300 × 300 像素的图像(360kb 的数据,可能压缩到小于 50-100kb),而不是 2,000 × 2,000 的资产(16mb 的数据,即使压缩也可能超过 1mb)。显然,这隐含的意思是,您也在下载合理压缩的资产。

    诚然,如果您尚未实现此功能,那么在不同大小的服务器上准备好资产是一项不平凡的工作,但设计良好的 Web 服务通常会 (a) 准备不同大小的资产;(b) 提供 API 来下载客户端应用程序所需的任何大小。

    如果下载要在小型集合视图单元格中显示的大型资产,这可能会对实际性能产生巨大影响。
     

  5. 压缩

    可能不用说(因为它很常见),但要确保服务器传输的资产被充分压缩。 例如,压缩质量为 70-80% 的 JPEG 看起来相当不错,但明显小于未压缩的资源。PNG 也提供压缩,但通常不提供与 JPEG 相当的大小(因为,除其他外,它是一种无损压缩)。

    最重要的是,确保服务器上的资产被合理压缩,在质量和大小之间取得一些平衡。恕我直言,某些服务在压缩资产、严重降低图像质量方面走得太远了(我正在和你说话,Facebook),但为您的应用程序选择合理的平衡。

因此,我支持对翠鸟等图书馆的其他建议。它简化了您的客户端代码,并自动交付了问题 #1,即缓存。但是问题 2 和 3,预取和取消,需要您做一些工作,但这很简单。问题 #4(和 #5)可能会产生最显著的影响(如果您还没有适当大小的资产),但您只能受 Web 服务的摆布。

两者都是相同的,因为它们是后台线程的变体,您最好使用 SDWebImage 将您的逻辑封装在缓存和 1 次加载的图像中,因为当用户在图像加载之前来回滚动时会发生这种情况

您可以使用异步方法

let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)

import KingfisherSwiftUI
var body: some View {
KFImage(URL(string: "https://example.com/image.png")!)
}

您可以使用 https://github.com/onevcat/Kingfisher 库

最新更新