ios - 在后台线程中为 Collection View 加载图像

标签 ios swift multithreading

所以我有一个 Collection View ,我在其中填充了 141 张图像,但我遇到的问题是在 ScrollView 时,它不是很流畅。我已经尝试过为单元格使用低分辨率图像(这略有帮助)并降低滑动速度(或者我应该说在滚动时增加减速)。

所以我认为我的下一步是尝试在后台线程上加载单元格?

首先,我不知道该怎么做,谷歌也帮不上什么忙。其次,我不确定我在单元格离开视野时重新使用单元格这一事实(因此我没有创建 141 个单元格,每个图像一个单元格)是否有所不同。

这是我加载图片的方式:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TreeCell", forIndexPath: indexPath) as? TreeCell {
        cell.contentView.backgroundColor = UIColor.clearColor()
        let tree = Tree(treeNumber: (indexPath.row + 1))
        cell.configureCell(tree)
        return cell
    }

    else {
        return UICollectionViewCell()
    }
} 

TreeCell 为任何人提问:

var tree: Tree!

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    layer.cornerRadius = 10.0
}

func configureCell(tree: Tree) {
    self.tree = tree

    treeCellImage.image = UIImage(named: "\(self.tree.treeNumber)")
}

还有树类:

private var _treeNumber: Int!

init(treeNumber: Int) {
    _treeNumber = treeNumber
}

var treeNumber: Int {
    return _treeNumber
}

最佳答案

不要尝试在后台线程上创建 UICollectionViewCell。单元格只能通过 dequeueReusableCellWithReuseIdentifier 变体创建,并且 UI 前缀类 (UIKit) 不是线程安全的,除非明确说明。正如您所说,它会破坏单元重用并可能导致各种内存问题。

查看这个 SO 问题:Load UIImage in background Thread

如果您不支持 iOS 8,您可以在后台从磁盘加载图像。载入后,我会在包含图像唯一 ID(文件名?)的主线程上发布 NSNotification。所有单元格都可以收听通知,并且 - 如果针对单元格的当前图像观察到通知 - 显示新的 UIImage

如果您可以使用 [UIImage imageNamed:],您将免费获得一些不错的低内存响应式缓存优势。否则,您可能需要维护已加载图像的缓存(NSCacheNSDictionary)。

如果你确实需要支持 iOS 8,你仍然可以在后台通过 NSData 从磁盘加载图像数据,并将图像数据提供给 UIImage主线程。昂贵的任务是从磁盘读取。如果您的图像在 Assets 目录中,这将比 imageNamed 需要更多的工作。

现在,假设您找到了一种在您的特定情况下可以在后台线程/队列上安全加载图像的方法,您有几个选项可以进一步优化性能:

限制并发任务

尝试限制并发图像加载的数量,否则快速滚动会使系统因阻塞任务和线程切换开销而饱和。

可取消任务

您应该能够取消挂起的图像加载操作。如果用户滚动的速度比您加载图像的速度快一点,您希望能够取消加载已经移出屏幕的图像。否则,您可能会用看不见的图像填满队列。

智能图片加载

不要加载将在几毫秒内滚动离开的图像(即响应“轻弹”或快速滑动)。您可以通过连接到 Collection View UIScrollViewDelegate 方法 来解决这个问题 -scrollViewWillEndDragging:withVelocity:targetContentOffset:。此方法让您知道用户拖动内容的速度以及 Collection View 将在何处结束。您甚至可以预加载目标内容偏移量附近的图像,以便在滚动结束时准备就绪!

NSOperationQueue 对这类事情有好处,因为 NSOperation 实例支持优先级排序、并发上限和并发执行。如果您使用 Grand Central Dispatch,则需要构建自己“取消”任务的能力。

关于ios - 在后台线程中为 Collection View 加载图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36510209/

相关文章:

ios - 在前台和后台无限运行任务

iphone - MPMoviePlayerController thumbnailImageAtTime :timeOption: simple case doesn't work

swift - 如何递增十六进制数

ios - 如何在iOS中找到线程状态?

python - python类中的简单线程管理

ios - 如何在 iOS 中将一个 Xcode 项目使用到另一个 Xcode 项目中?

ios - NSMutableArray 上的@synchronized 是否也对其元素产生同步效果?

swift - 如何以编程方式滚动 iOS WKWebView,swift 4

ios - 如何在 Swift 中对 NSProgress 进行键值观察

android - 从另一个线程访问 View 时出现不可预测的行为