16

我正在尝试在 iOS5 中同步以下代码:

  1. 一个对象有一个方法,它发出一个 HTTP 请求,从中获取一些数据,包括一个图像的 URL
  2. 数据到达后,文本数据用于填充 CoreData 模型
  3. 同时,异步调度第二个线程来下载图像;当图像已经缓存并在 CoreData 模型中可用时,该线程将通过 KVO 向 viewController 发出信号。
  4. 由于图像下载需要一段时间,我们立即将具有除图像之外的所有属性的 CoreData 对象返回给调用者。
  5. 此外,当第二个线程完成下载时,可以保存 CoreData 模型。

这是(简化的)代码:

- (void)insideSomeMethod
{
    [SomeHTTPRequest withCompletionHandler:
     ^(id retrievedData) 
     {
         if(!retrievedData)
         {
             handler(nil);
         }

         // Populate CoreData model with retrieved Data...

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
             NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
             aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
         });

         handler(aCoreDataNSManagedObject);
         [self shouldCommitChangesToModel];
     }];
}

- (void)shouldCommitChangesToModel
{
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSError *error = nil;
        if(![managedObjectContext save:&error]) 
        {
            //  Handle error
        }  
    });
}

但实际情况是基于屏障的保存块总是在图像加载块之前执行。那是,

dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSError *error = nil;
            if(![managedObjectContext save:&error]) 
            {
                //  Handle error
            }  
        });

之前执行:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                 NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
                 aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
             });

所以很明显我并没有真正在屏障之前调度图像加载块,或者屏障会等到图像加载块完成后再执行(这是我的意图)。

我究竟做错了什么?如何确保图像加载块在屏障块之前排队?

4

4 回答 4

29

乍一看,问题可能是您在全局并发队列上分派屏障块。您只能在自己的自定义并发队列上使用屏障块。根据 dispatch_barrier_async 上的 GCD 文档,如果您将一个块分派到全局队列,它将表现得像一个正常的 dispatch_async 调用。

Mike Ash 有一篇关于 GCD 障碍块的好博文:http: //www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

祝你好运

于 2012-05-30T00:25:28.600 回答
3

您需要创建自己的队列,而不是按照 ADC 文档分派到全局队列

您指定的队列应该是您使用 dispatch_queue_create 函数自己创建的并发队列。如果您传递给此函数的队列是串行队列或全局并发队列之一,则此函数的行为类似于 dispatch_async 函数。

来自https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async

你可以创建大量自己的 GCD 队列。gcd 队列非常小,您可以毫无问题地创建大量队列。当你完成它们时,你只需要释放它们。

于 2012-05-30T00:40:13.987 回答
0

对于您似乎试图解决的问题,dispatch_barrier_async可能不是最好的解决方案。查看并发编程指南的Migrating Away From Threads部分。仅dispatch_sync在您自己的串行队列上使用可能会解决您的同步问题。或者,您可以使用 NSOperation 和 NSOperationQueue。与 GCD 不同,NSOperation 允许您轻松地管理依赖关系(您可以使用 GCD 来完成,但它会很快变得丑陋)。

于 2012-06-18T07:16:59.223 回答
0

我参加聚会有点晚了,但也许下次你可以尝试使用dispatch_groups 来发挥你的优势。http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

于 2015-05-21T05:03:09.980 回答