【问题标题】:iPhone - GCD doesn't work for the first timeiPhone - GCD 第一次不工作
【发布时间】:2013-03-22 18:54:28
【问题描述】:

在我的应用中,我从 URL 加载图像:

-(void)loadImages
{
    ...

    image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl1]];
}

为了避免在下载完成之前阻塞主线程,我在 -viewDidAppear 使用GCD:

dispatch_async( dispatch_get_global_queue(0,0), ^{
            [self loadImages];
        });

但是,当我第一次用imageView 打开我的view controller 时,imageView 是空的(即使我等待了很长时间)但是当我再次打开这个view controller 并出现图像后一切顺利。

我的错误在哪里? 抱歉,多线程新手 :)

编辑: 我也忘了提一下,我得到它时使用了tableView中的图像:

 cell.imageView.image = image1;

【问题讨论】:

  • dispatch_get_global_queue 不是 UI 更新所需的主队列。请改用dispatch_async(dispatch_get_main_queue(), ^{ ... });。有关信息,请参阅此答案:stackoverflow.com/q/9602042/558933

标签: iphone ios objective-c xcode cocoa


【解决方案1】:

您需要通知视图图像需要重绘。添加:

[imageView setNeedsDisplay];

到您的 loadImages 方法结束。

【讨论】:

  • 对不起,我忘了说我在cell.imageView中使用了这张图片下一个:cell.imageView.image = image1;
【解决方案2】:

iOS 中的 UIElements 应该始终通过主线程进行更新。 你可以做的是:-

   __block NSData *data;
    dispatch_queue_t myQueue = dispatch_queue_create("com.appName", NULL);
    dispatch_async(myQueue, ^{
       data = [NSData dataWithContentsOfURL:imgUrl1];
       dispatch_async(dispatch_get_main_queue(), ^(void) {
       cell.imageView.image = [UIImage imageWithData = data];
    });
    });

否则,您有更好的方法可以使用 AFNetworking 从 URL 获取图像。它更快更容易。 你只需要写一行代码:-

[cell.imageView setImageWithURL:imgUrl1];

【讨论】:

  • 这不太正确。 dispatch_async(dispatch_get_main_queue(), ...) 必须在第一个块内完成(在获取数据之后)。
  • 是的。否则,您将依赖myQueue(此处未显示其声明)来分派块和块以从您拥有的任何类型的 URL 中成功检索数据,所有这些都在第二个块被分派之前完成。这可能有效,也可能无效,具体取决于两个队列的繁忙程度。 (还有一个语法错误。)
【解决方案3】:

这可能不是您要寻找的答案,但这不是加载 URL 的推荐方式。您应该使用可用的 URL 加载类,例如 NSURLRequest 和 NSURLConnection。

试试这个:

NSURLRequest *imageRequest = [[NSURLRequest alloc] initWithURL:imageURL];
[NSURLConnection sendAsynchronousRequest:imageRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if (!error) {
        UIImage *image = [[UIImage alloc] initWithData:data];
        [imageView setImage:image];
    }
}];

【讨论】:

  • 您应该使用[NSOperationQueue mainQueue] 而不是自定义操作队列,特别是因为您正在更新块中的视图。
【解决方案4】:

由于是在TableView中使用的,添加[self.tableView reloadData];在 loadImages 方法的末尾。

【讨论】:

    【解决方案5】:

    这里有很多问题:

    1. 您无法在未运行 runloop 的线程上启动网络请求。
    2. 您不能从主线程以外的线程更新您的 UI。
    3. [NSData dataWithContentsOfURL:imgUrl1] 不是加载外部资源的安全方式,即使在不同的线程上(尤其是在主线程上)也是如此。
    4. 每当您分派到不同的线程时,您都会面临表格单元格已被回收并且不再显示您认为的数据的风​​险。 (它仍然是同一个单元格实例,但现在显示了一些其他行的数据。)

    你应该做什么:

    1. 在主线程上使用异步调用开始您的网络操作。 (如果需要,您可以使用另一个线程或队列,但您需要确保它正在运行一个 runloop。)
    2. 从您的委托消息中,将您的图像解码分派到不同的线程。
    3. 图像解码后,分派回主线程进行更新。
    4. 在实际分配图像之前,请检查单元格是否仍在用于您认为的目的。

    您可以使用AFNetworking 解决前三个问题。它包装了委托方法,让您只提供一个成功和失败块。正如我所描述的,AFNetworking 的AFImageRequestOperation 特别是在队列之间反弹代码。 (它甚至在不同的线程中运行它的主网络循环,这不是必要的,但既然它做得很好,为什么不呢?)

    您仍然需要验证单元格的身份。

    【讨论】:

      猜你喜欢
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-21
      • 2012-05-16
      • 1970-01-01
      相关资源
      最近更新 更多