2

我正在尝试从 API 下载数据,然后再将其显示给用户。

这就是我正在做的事情:

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{

    __block NSMutableArray *newImages;

    dispatch_sync(concurrentQueue, ^{
        newImages = [NSMutableArray array];
        // images retrieved using `NSURLConnection sendSynchronousRequest`
    });

    dispatch_sync(dispatch_get_main_queue(), ^{
        // display images to the user
    });
});

我的问题是,既然newImages是用 声明的__block,我是否总是保证newImages在主队列执行第二个任务时拥有最新的数据?如果不是,那么将该数组的内容传递到第二个块并确保其最新内容的最安全方法是什么?

我认为我的数据在某个地方搞砸了,因此我问这个问题。

如果这看起来不错,那么我将发布我的完整代码以寻求帮助,以防万一出现其他问题。

4

4 回答 4

2

解决方案的草图:

q = /* custom serial queue */
urls = /* urls array */;
NSMutableArray *images = [NSMutableArray new];
for (NSURL *url in URLs) {
   NSURLRequest *req = [self imageRequestForURL:url];
   dispatch_async(q, ^{
     UIImage *image = [self imageFromRequest:req];
     [images addObject:newImage];
   }
}
dispatch_async(q, ^{
   dispatch_async(dispatch_get_main_queue(), ^{
     [self applyUpdatesForURLs:urls withImages:images];
   });
}

该队列是标准工作队列。

由于applyUpdatesForURLs:withImages:在图像下载块之后被排入串行队列,因此可以保证在下载所有图像后运行该块。

没有同步问题,images因为所有使用它的代码都串行运行而不是同时运行。

而 UI 更新最终发生在主线程上。

于 2012-10-01T23:43:12.850 回答
1

首先,您不能dispatch_sync()在并发队列上。(嗯,你可以,但它与 a 完全相同dispatch_async()。)dispatch_sync()只对串行队列有任何概念意义,你说,“我想等到所有在我之前排队的块结束,然后执行这个块,然后将控制权返回给调用线程。”

其次,rdelmar 的回答是正确的——你把它复杂化了。即使您不想在 上使用批处理完成处理程序NSURLConnection,您当然也不需要在并发队列上嵌套两个块调度 - 只需一个块执行批处理下载(在并发队列上运行异步)与嵌套完成后主队列上的 UI 更新块就好了!

于 2012-10-01T23:25:24.710 回答
1

你不能在一个并发队列中有多个块来更新一个可变数组——它只是不能正常工作,因为可变容器不是线程安全的。当图像可用时,在主队列中排队一个块,然后将其添加到那里的数组中。

于 2012-10-01T21:21:42.697 回答
1

您发布的方法看起来过于复杂。为什么不使用 NSURLConnection 方法,sendAsynchronousRequest:queue:completionHandler:。您可以指定 mainQueue 作为队列参数来更新您的 UI。

    NSURL *url = [NSURL URLWithString:@"your server address"];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *theData, NSError *error){
        if (error == nil) {
            //do what you want with theData and update the UI
        }
    }];
于 2012-10-01T21:17:16.893 回答