4

在我的应用程序中,我需要从多个来源加载数据并将它们放在一个表格视图中。一个接一个地收集每个来源将花费很长时间。为了解决这个问题,我需要一起运行所有下载操作。由于它们是下载任务,理论上我可以运行它们,但问题是线程上只有部分代码异步运行,这意味着它需要主线程来完成操作。

因此,为了让所有这些都在后台运行,我需要使用 GCD,我对此并没有太多经验。

//DataLoader.m

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    [self.webLoader getFeedWithCompletion:self.thatOtherCompletionBlock];
    [self.otherDataLoader getDataWithCompletion:self.completionBlock];
    [self.thatDataLoader getThatDataWithCompletion:self.anotherCompletionBlock]
    dispatch_async(dispatch_get_main_queue(), ^(void){

    });
});

但是,由于部分任务已经是异步的,我需要弄清楚将 GCD 代码放在哪里。

我可以在开始任务之前把它放在上面,就像我上面做的那样。但是,这可能会起作用,因为任务已经部分在后台运行(在某些情况下我无法更改),在后台运行已经部分在后台运行的任务似乎很浪费。为什么要在另一个线程中运行已经在后台线程中运行的东西?

另一种选择是在获取提要的实际类(例如 webloader)中使用 GCD,将其放在所有未在后台运行的代码上

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    .......

 });

哪种方式更好?

还有另一个问题。由于部分任务是异步的,因此它们使用完成块。我不仅需要在后台运行完成块,我还需要弄清楚哪一个是最后一个完成的,所以我可以运行一些代码来清理和整齐地打包数据并将数据发送到视图控制器。

我想到的方法是为每个任务使用一个 BOOL ,只需在完成后将其更改为 true 。然后在我的完成块中,我可以检查所有其他任务是否已完成,如果是,则运行清理代码。然而,这可能不是最优雅的解决方案。

处理这些任务的最佳方式是什么,确保这一切都在后台发生?

4

1 回答 1

5

GCD 组可以很容易地用于此目的。组允许您跟踪组的任意“成员”,并在组的所有成员完成时连接一个块以运行。这很方便。例如(使用您的代码):

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group); // + 1
    [self.webLoader getFeedWithCompletion: ^{
        self.thatOtherCompletionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_enter(group); // + 1
    [self.otherDataLoader getDataWithCompletion:^{
        self.completionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_enter(group); // + 1
    [self.thatDataLoader getThatDataWithCompletion:^{
        self.anotherCompletionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_notify(group, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // This will get executed once all three of the prior completion blocks have been run.
        // i.e. when the group "count" goes to zero.
    });

    dispatch_release(group);
});

尽管有点迂回,您也可以使用 NSOperation 的互操作依赖特性来实现这一点。像这样:

NSOperationQueue* q = [[[NSOperationQueue alloc] init] autorelease];

NSOperation* completionA = [NSBlockOperation blockOperationWithBlock: self.thatOtherCompletionBlock];
NSOperation* completionB = [NSBlockOperation blockOperationWithBlock: self.completionBlock];
NSOperation* completionC = [NSBlockOperation blockOperationWithBlock: self.anotherCompletionBlock];

NSBlockOperation* afterAllThree = [[[NSBlockOperation alloc] init] autorelease];
[afterAllThree addDependency: completionA];
[afterAllThree addDependency: completionB];
[afterAllThree addDependency: completionC];
[afterAllThree addExecutionBlock:^{
    // This will get executed once all three of the prior completion blocks have been run.
}];

// Kick off the tasks
[q addOperationWithBlock:^{
    [self.webLoader getFeedWithCompletion: ^{ [q addOperation: completionA];}];
    [self.otherDataLoader getDataWithCompletion:^{ [q addOperation: completionB]; }];
    [self.thatDataLoader getThatDataWithCompletion:^{ [q addOperation: completionC]; }];
}];

我个人更喜欢这种dispatch_group方法,但他们都会完成工作。

于 2013-08-19T12:23:14.040 回答