我目前正在做一个实践应用程序,并在其中使用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的线程上被调用?
发布于 2015-09-14 00:35:07
首先,您需要将创建调度队列的行移到此方法之外。
您可能应该只在程序生命周期内创建一次调度队列,或者至少为视图控制器生命周期创建一次。
可以将filterQueue作为视图控制器的实例变量,然后将创建队列的代码移动到viewDidLoad方法中。
然后,您需要向视图控制器添加一个dealloc方法,并在调度队列上调用dispatch_release。
现在问你的问题
dispatch_async提交用于处理调度队列的代码块。在本例中,您使用dispatch_queue_create()创建了目标队列。如果读取该函数的文档--他们说,如果为第二个参数传入NULL,则会得到一个串行队列。串行队列一次只能运行一个任务,按FIFO顺序运行。
因此,是的,您要求在串行队列中运行代码块,串行队列运行在后台线程上。
该代码调用您的方法filteredImageFromImage,您需要确保它是线程安全的,并且不使用/设置self的任何实例变量。
一旦对filteredImageFromImage的调用完成,就可以使用对dispatch_async(dispatch_get_main_queue())的调用。它说:“从我的后台线程,提交一个代码块来运行在主队列上(它运行在主线程上)。”
您必须这样做,因为您不能从后台线程更改UI对象。几乎没有一个UIKit是线程安全的。如果代码对视图进行了更改,则需要在主线程上执行。
可以考虑使用主队列以外的队列调用dispatch_async,就像要求助手为您做一些工作一样,同时还要继续自己的工作。助手在后台线程上运行。
你让助手告诉你什么时候完成工作,这样你就可以把它安装在你的视图中。对dispatch_async(dispatch_get_main_queue())的调用就是这样做的。它在主线程上有后台线程调用代码。
注意,您的代码在编写时有一个潜在的问题。
如果对filteredImageFromImage的调用仍在运行,并且用户将此单元格滚动到屏幕外,则该单元格将被回收,以在集合视图中显示一个不同的条目。但是,当filteredImageFromImage完成时,生成的映像将被安装到"cell“中,该单元格被回收,现在正在不同的indexPath上显示数据。
相反,您应该在指定的索引路径上询问单元格的集合视图,如果它仍然可用,则应该安装该映像:
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可能会降低设备的响应能力,因此应该谨慎使用。
发布于 2015-09-14 00:49:32
分派队列是一种抽象类型,与线程模型分离。您所知道的有关调度队列的全部知识是,它们可以分派任务。这些任务与来自其他队列的任务并行运行。
当您调用dispatch_async时,基本上是否切换到我声明的队列,或者换句话说,在一个单独线程上的filterQueue?
除了filterQueue上分派的任务与dispatch_get_main_queue()上分派的任务并行运行外,您不能得到任何关于任务的保证。
第二个dispatch_async (切换到主队列)是否在我最初创建的自定义filterQueue的线程上被调用?
除了dispatch_get_main_queue()上分派的任务与filterQueue上分派的任务并行运行外,您不能得到任何关于任务的保证。
现在,有了所有的API保证,让我们进入当前的实现。
在主队列上运行的所有任务都位于一个线程中:主线程。创建filterQueue时,将为其分配一个线程池。这些线程中的任何一个都可以运行分派给filterQueue的任务。注意:这个线程池将永远不会包含主线程。
现在,这两个问题的答案应该是显而易见的。在filterQueue上分派的任务在某个不是主线程的线程上运行。在dispatch_get_main_queue()上分派的任务在主线程上运行。
https://stackoverflow.com/questions/32555817
复制相似问题