ios - Async_Dispatch 线程

标签 ios objective-c multithreading

我目前正在制作一个练习应用程序并在其中使用 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/

相关文章:

ios - XCUITest 等待其中包含特定静态文本的单元格

ios - 将 UIView 从一个选项卡移动到另一个选项卡

objective-c - 核心数据 : error: Failed to call designated initializer on NSManagedObject class

ios - 在框架和使用框架的应用程序之间使用 Google maps sdk

ios - iOS swift 中使用 ColorMatchTabs 的标签栏动画

ios - 当应用程序重新启动到后台时,是否会调度主队列?

objective-c - 对象释放问题

multithreading - Chapel、语言环境和多核多处理器机器

c - 将带有数组的结构传递给多个线程

java - android中的main或UI线程在哪里准备弯针?