我目前正在制作一个练习应用程序并在其中使用 GCD,但在理解某些方面时遇到了一些大麻烦。
我对这段代码有疑问:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TWPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
//cell.backgroundColor = [UIColor clearColor];
dispatch_queue_t filterQueue = dispatch_queue_create("filter queue", NULL);
dispatch_async(filterQueue, ^{
UIImage *filterImage = [self filteredImageFromImage:self.photo.image usingFilter:[self.filters objectAtIndex:indexPath.row]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = filterImage;
});
});
return cell;
}
当您调用 dispatch_async 时,是否基本上切换到我声明的队列,或者换句话说,切换到单独线程上的 filterQueue?此外,我在第一个 dispatch_async 中调用 dispatch_async 以返回主队列以进行一些 UI 更改。第二个 dispatch_async (我切换到主队列的地方)是否在我最初创建的自定义 filterQueue 的线程上被调用?
最佳答案
首先,您需要将创建调度队列的行移到该方法之外。
您应该在程序的生命周期内只创建一次调度队列,或者至少在 View Controller 的生命周期内只创建一次。
您可以将 filterQueue 设为 View Controller 的实例变量,然后将创建队列的代码移至您的 viewDidLoad 方法。
然后您需要向您的 View Controller 添加一个 dealloc 方法,并在您的调度队列上调用 dispatch_release。
现在关于您的问题:
dispatch_async 提交一个代码块以在调度队列上进行处理。在本例中,您使用 dispatch_queue_create() 创建了目标队列。如果您阅读该函数的文档,他们会说如果您为第二个参数传入 NULL,您将获得一个串行队列。串行队列一次只会以 FIFO 顺序运行 1 个任务。
是的,您要求您的代码块在您的串行队列中运行,该队列在后台线程上运行。
该代码调用您的方法 filteredImageFromImage
,您需要确保该方法是线程安全的,并且不使用/设置 self 的任何实例变量。
完成对 filteredImageFromImage
的调用后,您可以调用 dispatch_async(dispatch_get_main_queue())
。这表示“从我的后台线程,提交一段代码以在主队列上运行(在主线程上运行。)”
您必须这样做,因为您无法从后台线程更改 UI 对象。几乎没有 UIKit 是线程安全的。如果代码对 View 进行更改,则需要在主线程上完成。
将使用主队列以外的队列调用 dispatch_async 视为要求助手为您做一些工作,同时您继续做自己的事情。助手在后台线程上运行。
您要求助手在工作完成时通知您,以便您可以将其安装在您的 View 中。这就是对 dispatch_async(dispatch_get_main_queue())
的调用所做的。它在主线程上有后台线程调用代码。
请注意,您的代码在编写时存在潜在问题。
如果对 filteredImageFromImage
的调用仍在运行并且用户将此单元格滚动到屏幕外,则该单元格将被回收以在您的 Collection View 中显示不同的条目。然而,当 filteredImageFromImage
完成时,生成的图像将被安装到“cell”中,它被回收并且现在在不同的 indexPath 中显示数据。
相反,您应该向 Collection View 询问指定索引路径处的单元格,如果它仍然可用,那么您应该安装图像:
dispatch_async(filterQueue, ^{
UIImage *filterImage = [self filteredImageFromImage:self.photo.image usingFilter:[self.filters objectAtIndex:indexPath.row]];
dispatch_async(dispatch_get_main_queue(), ^
{
//Ask the collection view for the target cell
UICollectionViewCell targetCell = [collectionView cellForItemAtIndexPath: indexPath];
if (targetCell != nil)
targetCell.imageView.image = filterImage;
}
);
});
编辑:
请注意,创建自己的队列并不常见。使用现有的调度队列之一更常见、更容易并且通常更有效地使用系统资源。您可以使用函数 `dispatch_get_global_queue() 来获取现有队列。您可以使用 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT) 或 DISPATCH_QUEUE_PRIORITY_HIGH。但是,使用 DISPATCH_QUEUE_PRIORITY_HIGH 可能会导致设备响应速度变慢,因此应谨慎使用。
关于ios - Async_Dispatch 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32555817/