Ios 在后台线程中加载集合视图的图像

Ios 在后台线程中加载集合视图的图像,ios,swift,multithreading,Ios,Swift,Multithreading,所以我有一个集合视图,我在其中填充了141个图像,但我遇到的问题是当滚动视图时,它不是很平滑。我已经尝试过对单元格使用低分辨率图像(这有点帮助)并降低滑动速度(或者我应该说在滚动时增加减速) 所以我想我的下一步是尝试在后台线程上加载单元格 首先,我不知道怎么做,谷歌也帮不了什么忙。第二,我不确定在看不见的情况下使用单元格(所以我不创建141个单元格,每个图像一个单元格)是否会产生影响 下面是我在图像中加载的方式: func collectionView(collectionView: UICol

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

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

首先,我不知道怎么做,谷歌也帮不了什么忙。第二,我不确定在看不见的情况下使用单元格(所以我不创建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
s。单元格只能通过
dequeueReusableCellWithReuseIdentifier
变量创建,并且
UI
前缀类(
UIKit
)不是线程安全的,除非明确说明。正如您所说,它将破坏单元重用,并可能导致各种内存问题

看看这个问题:

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

如果您可以使用
[UIImage imageNamed::
,您将免费获得一些不错的低内存反应式缓存好处。否则,您可能需要维护已加载图像的缓存(
NSCache
NSDictionary

如果您确实需要支持iOS 8,您仍然可以通过
NSData
从后台磁盘加载图像数据,并将图像数据馈送到主线程上的
UIImage
。昂贵的任务是从磁盘读取数据。如果您的图像位于资产目录中,这将需要比
imageNamed
更多的工作

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

Cap并发任务 尝试限制并发映像加载的数量,否则快速滚动会使系统充满阻塞任务和线程切换开销

可取消的任务 您应该能够取消挂起的映像加载操作。如果用户的滚动速度略快于您加载图像的速度,则您希望能够取消已移出屏幕的图像的加载。否则,您可以用看不见的图像填充队列

智能图像加载 不要加载将在毫秒内滚动掉的图像(即响应“轻弹”或快速滑动)。您可以通过连接到集合视图
UIScrollViewDelegate
方法

-ScrollViewWillendDraging:withVelocity:targetContentOffset:
。通过此方法,您可以了解用户拖动内容的速度以及集合视图的最终位置。您甚至可以在目标内容偏移量附近预加载图像,以便在滚动结束时准备就绪


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

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

看看这个问题:

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

如果您可以使用
[UIImage imageNamed::
,您将免费获得一些不错的低内存反应式缓存好处。否则,您可能需要维护已加载图像的缓存(
NSCache
NSDictionary

如果您确实需要支持iOS 8,您仍然可以通过
NSData
从后台磁盘加载图像数据,并将图像数据馈送到主线程上的
UIImage
。昂贵的任务是从磁盘读取数据。如果您的图像位于资产目录中,这将需要比
imageNamed
更多的工作

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

Cap并发任务 尝试限制并发映像加载的数量,否则快速滚动会使系统充满阻塞任务和线程切换开销

可取消的任务 您应该能够取消挂起的映像加载操作。如果用户的滚动速度略快于您加载图像的速度,则您希望能够取消已移出屏幕的图像的加载。否则,您可以用看不见的图像填充队列

智能图像加载 不要加载将在毫秒内滚动掉的图像(即响应“轻弹”或快速滑动)。您可以通过连接到集合视图
UIScrollViewDeleg来解决这个问题