用于将图像加载到单元格的NSOperation - 崩溃

时间:2017-03-04 00:17:18

标签: ios swift nsoperationqueue

我使用NSOperation下载每个单元格的图像,因此我不会因重载图像而使用户过载。这种情况有99%的时间可以正常运行,但有时候我的操作块内部会出现nil并且应用程序崩溃。

这是代码:

    cell.blockImage.image = nil
    cell.queue.cancelAllOperations()

    let cacheKey = indexPath.row

    if(self.imagesDictionary.object(forKey: cacheKey) != nil)
    {
        cell.blockImage.image = self.imagesDictionary.object(forKey: cacheKey) as? UIImage
    }
    else
    {
        let operation: BlockOperation = BlockOperation()
        operation.addExecutionBlock({

            if let url = NSURL(string: self.arrJSONData[indexPath.row].image) {
                if let data = NSData(contentsOf: url as URL) {
                    if let image: UIImage = UIImage(data: data as Data)
                    {
                        self.imagesDictionary.setObject(image, forKey: cacheKey as NSCopying)
                        DispatchQueue.main.async(execute: {

                            if(operation.isCancelled)
                            {
                                return
                            }

                            cell.blockImage.image = image
                        })
                    }
                }
            }



        })

        cell.queue.addOperation(operation)

    }

所以我下载de image并将其存储在字典中。关键是单元格的indexPath.row。

我还验证图像是否已经在字典中,所以我不再下载它。

当我开始加载单元格时,我总是将我的图像设置为nil并取消BlockOperation。

我总是得到的错误是:

malloc: *** error for object 0x608000244c50: Invalid pointer dequeued from free list
*** set a breakpoint in malloc_error_break to debug

我总是把它放在这个区块内(但是这条线总是随机的)

if let image: UIImage = UIImage(data: data as Data)
{
    self.imagesDictionary.setObject(image, forKey: cacheKey as NSCopying)
    DispatchQueue.main.async(execute: {

        if(operation.isCancelled)
        {
            return
        }

        cell.blockImage.image = image
    })
}

我做错了什么?感谢。

1 个答案:

答案 0 :(得分:1)

有几个问题:

  1. 如果您使用NSMutableDictionary,则必须同步与该词典的所有互动。 NSMutableDictionary不是线程安全的。如果使用具有非常相似接口的NSCache,它提供了线程安全交互,因此不需要手动同步。

  2. 此外,您不应只更新操作中的cell。您不知道与该索引路径关联的单元格是否仍然可见(或者更糟糕的是,它是否已被重用于其他索引路径)。您应该使用cellForRow(at:)(不要与类似名称的UITableViewDataSource方法混淆)来获取与IndexPath关联的当前单元格。如果返回非nil单元格引用,您应该使用它来更新单元格的图像。如果是nil,则无法更新可见UIImageView

  3. 与您的崩溃无关,如果您快速滚动查看表格视图,您的网络请求队列可能会积压下载图片,以查看不再可见的单元格。例如,如果快速滚动到第100行,则对可见单元格的请求可以在对不再可见的前99行的请求后面进行积压。如果它们具有低速网络连接(您应该尝试使用“网络链路调节器”进行模拟),则会放大此问题。

    这使得更成问题的原因是您正在使用同步的,不可取消的网络请求。如果您(正如EricD所建议的那样)使用UIImageView扩展之一进行异步图像检索(例如AlamofireImageKingfisher等等;那里有许多扩展名,可以减轻这个问题