Objective-C - 等待两个异步方法完成

6

我想按照同步顺序调用四种方法,前两个方法是同步的,后两个方法是异步的(从网址获取数据)。

伪代码:

- (void)syncData {

    // Show activity indicator
    [object sync]; // Synchronous method
    [object2 sync]; // Synchronous method
    BOOL object3Synced = [object3 sync]; // Async method
    BOOL object4Synced = [object4 sync]; // Async method
    // Wait for object3 and object4 has finished and then hide activity indicator
}

我该如何实现这个目标?

3
这里不适合使用伪代码。为了帮助,我们需要知道您使用的是哪种异步机制。 - Nikolai Ruhe
2
@PeterWarbo 如果 object3object4 有一些接口可以在它们的操作完成时通知您,那么您需要编辑您的帖子并展示给我们这个接口。 - rob mayoff
object3正在通过NSURLRequest下载文件,而object4正在使用Facebook SDK下载数据(我相信在幕后也是使用NSURLRequest)。 - Peter Warbo
4个回答

25

使用屏障:

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

提交一个屏障块以进行异步执行并立即返回。

当屏障块到达私有并发队列的前面时,它不会立即执行。相反,队列等待其当前正在执行的块完成执行。然后,队列单独执行屏障块。在屏障块完成之前,提交的任何块都不会执行。

此示例输出1 2 3 4 done,尽管是异步的,但实际上也可能是1 2 4 3 done。由于我理解您想要处理活动指示器,因此这无关紧要。

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        dispatch_queue_t queue = dispatch_queue_create("com.myqueue", 0);
        dispatch_sync(queue,  ^(){NSLog(@"1");} );
        dispatch_sync(queue,  ^(){NSLog(@"2");});
        dispatch_async(queue, ^(){NSLog(@"3");});
        dispatch_async(queue, ^(){NSLog(@"4");});
        dispatch_barrier_sync(queue, ^(){NSLog(@"done");});
    }
}

寻找其他测试异步代码的方法,请参见:https://dev59.com/N3RA5IYBdhLWcg3wzhVY#11179523


如果您不介意异步方法的并发执行,请在创建队列时使用DISPATCH_QUEUE_CONCURRENT(在iOS 5中可用)而不是0。 - Jano
假设这两个异步操作都在同一个队列上,并且该队列可以从调度屏障的代码访问。 - Peter Hosey

5
假设您已经有一种方法来知道异步方法何时完成,您可能想要的是类似以下内容的东西:
- (void)syncData {

    // Show activity indicator
    [object sync]; // Synchronous method
    [object2 sync]; // Synchronous method

    _object3Synced = _object4Synced = NO;
    [object3 syncWithCompletionHandler:
          ^{
               _object3Synced = YES;
               [self considerHidingActivityIndicator];
          }]; // Async method
    [object4 syncWithCompletionHandler:
          ^{
               _object4Synced = YES;
               [self considerHidingActivityIndicator];
          }]; // Async method
}

- (void)considerHidingActivityIndicator
{
     if(_object3Synced && _object4Synced)
     {
         // hide activity indicator, etc
     }
}

这正是我想象中的实现方式,使用块完成处理程序...感谢您的建议。 - Peter Warbo
+1 - 当然,object4方法不能依赖于对object3方法调用的处理... - bryanmac

3
您可以创建一个UIActivityInidicator的子类,添加一个activityCount属性,并实现以下两个额外的方法:
- (void)incrementActivityCount
{
    if(_activityCount == 0)
    {
        [self startAnimating];
    }
    _activityCount++;
}

- (void)decrementActivityCount
{
    _activityCount--;
    if(_activityCount <= 0)
    {
        _activityCount = 0;
        [self stopAnimating];
    }
}

现在,每当您启动使用活动计数器的东西时,请调用 incrementActivityCount,并在其完成块(或其他情况下完成时)调用 decrementActivityCount。您可以在这些方法中进行其他操作,上述内容只是一个简单的示例,在大多数情况下可能已足够(特别是如果设置了hidesWhenStopped = YES)。

2
@Daij-Djan 当然,这些应该在主线程上调用,任何涉及 UI 的操作都应该如此。每当你处理完成块以更新 UI 时,应确保它们被分派到主队列。 - jhabbott

0
你需要启动第一个异步方法并使用完成块。在第一个异步方法的完成块中,你将启动第二个异步方法。虽然这使得使用异步方法有点无关紧要。

是的,您会失去使两个异步调用并行的好处,但您不会失去所有好处 - 至少它已经远离 UI 线程且不会挂起。如果第二个异步方法依赖于第一个的处理,则这是正确的方法。否则,其他答案更好。 - bryanmac
没错,顺便提一下我没有给问题投反对票 - 提问者的问题含糊不清且伪代码混杂。此外,如果它们彼此有依赖关系,你的答案更好。 - bryanmac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接