如何检查dispatch_async是否为相同的操作提前调用

时间:2015-03-18 20:33:15

标签: ios iphone multithreading dispatch-async

我遇到了问题。我的应用程序从服务器下载图像并显示在UITableView下载我正在使用的图像dispatch_async

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // downloading image from server
    dispatch_async(dispatch_get_main_queue(), ^{
        // update UI for showing downloaded image
    });
});

现在的问题是,如果用户非常快速地向上和向下滚动,那么同一图像会激活多个dispatch_async。我有图片ID。有没有办法通过图片ID检查dispatch_async是否因该图像被解雇?

3 个答案:

答案 0 :(得分:1)

您必须设置自己的机制来跟踪是否有任何针对该图像的待处理请求(例如,由图像URL绝对字符串键入的字典或类似的字典)。问题是,它并不那么简单,因为现在需要更新的单元格可能与最初请求它的单元格不同(或者除此之外)。这很快变得丑陋。

但是我退后一步,询问这是根本问题,还是更广泛问题的症状。例如,如果用户相对快速地向下滚动到第100行。该图像请求是否在前99行后面积压?当你处于这种情况时,你描述的问题开​​始变得严重,因为如果你现在在其他99个图像下载完成之前快速回滚到顶部,你将发出请求以获取它们一次。

但是,如果您更改异步图像检索以取消对不可见的单元格的请求,那么这个严重的问题突然变得有些学术化。此外,您还可以获得额外的好处,即用户快速滚动到的第100行的图像不会在所有其他图像请求后面积压,因此它看起来很快。

这意味着您要使用可取消的异步请求。因此,如果您必须支持早于NSURLSession的操作系统版本,则建议使用NSURLConnection(或基于委托的NSURLSession。此外,这通常会使人倾向于操作队列和子类化的异步NSOperation子类而不是您的问题所建议的GCD队列(使它们可取消,异步,并且还限制并发度)。然后,您必须实现使用您辛勤创建的所有此取消逻辑的代码(例如UIImageView类别或某个图像请求管理器,您的UITableViewDataSource和/或{{ 1}}子类使用)。

此外,您希望确保使用缓存机制(可能同时用于内存和持久存储)。这样,如果您已经检索到特定图像,当您向后滚动到该行时,图像就可以让您检索它,而不需要其他网络呼叫。

我知道你已经说过你想自己做这件事,但这是一项非常重要的工作。这就是为什么我(和其他人)建议您考虑签出允许您异步检索图像的UITableViewCell类别之一。查看SDWebImageAFNetworking提供的UIImageView类别。如果您正确地将单元格出列,那么这些可以在异步检索图像时实现非常灵敏的UI。它们有效地取消了对重用细胞的请求,从而优先考虑可见细胞。它们还会缓存结果,从而在您向后滚动时获得良好的性能。

答案 1 :(得分:0)

为此,您应该将下载的图像存储在某个位置,或者标记是否存在当前操作。所以你将基本上做一个图像缓存机制。我会建议你这样做,你可以使用经过良好测试的库,就像SDWebImage那样精确处理下载图像并将它们缓存到内存或磁盘。

答案 2 :(得分:0)

保持简单:使用NSURLSession而不是自己尝试异步URL提取。依靠缓存来避免重复下载以前的下载完成。然后,您需要担心的是重复下载当前正在进行的请求。

最容易的事情可能就是保留两个词典:NSURLSessionDataTask的URL和完成处理程序数组的URL。

当一个单元格想要一个URL时,它会调用这两个词典的处理程序,提供一个完成处理程序。

如果该URL没有数据任务,则处理程序创建一个并启动它,同时创建一个仅包含该完成处理程序的字典。两者都被放入各自的词典中。

如果该URL有数据任务,则完成处理程序将添加到第二个字典中的数组中。

使用调用处理程序的完成块创建任务,收到后将从第一个字典中删除任务并调用第二个字典中数组中的所有内容,然后从那里删除数组。

鉴于您正在查看相关内容,处理程序仅允许来自主队列的外部调用。它将确保在完成的数据任务之后的工作也在主队列上执行。这意味着您没有并发问题。