在 iOS 中等待多个网络异步调用

Posted

技术标签:

【中文标题】在 iOS 中等待多个网络异步调用【英文标题】:Waiting for several networking async calls in iOS 【发布时间】:2013-08-01 04:03:09 【问题描述】:

从视图控制器中,作为按钮操作的结果,我需要创建一个自定义对象来管理一组异步远程服务调用,并调用触发这些服务调用的此类对象的方法。我需要视图控制器等待所有异步网络操作完成才能更新其视图。由于网络操作是异步的,我不知道在所有操作完成后如何从管理此任务的自定义对象与视图控制器进行通信。

这是我目前拥有的代码。视图控制器中的代码sn-p是这样的(resultvar目前没有使用):

- (void)loadData

   BOOL __block result = NO;

   dispatch_queue_t queue = dispatch_queue_create(dataLoadQueue, NULL);
   dispatch_async(queue,^

      Loader *loader = [[Loader alloc] init];
      [loader loadData];

      dispatch_async(dispatch_get_main_queue(), ^

        if (result) 
            // Update view and notify success
        
        else 
            // Update view and notify error
        
      );
   );

   dispatch_release(queue);

这是loader自定义对象端:

- (void)loadData

   if ([Reachability checkNetStatus]) 

      Service1 *service1 = [[Service1 alloc] init];
      [service1 callAsyncService];

      Service2 *service2 = [[Service2 alloc] init];
      [service2 callAsyncService];

      // More service calls
   

   else 
      // Notify network not reachable
   

对象service1, service2... serviceN 符合NSURLConnectionDelegate 并且我通过NSNotificationCenter 通知它们已完成connectionDidFinishLoading:loader 对象正在侦听此类通知)。然后,我不知道让loader 等待所有网络操作并通知视图控制器的正确方法是什么。

提前致谢

【问题讨论】:

加载器是否总是创建相同的服务对象集?从您发布的代码中看起来很像,因为 Loader 的 loadData 方法没有参数。 @rdelmar 是的,确实如此 【参考方案1】:

您可能有很多方法可以做到这一点。首先,我认为没有必要在视图控制器中使用 GCD —— loader 已经在异步执行操作,所以 loader 的创建速度很快。

至于 Loader 如何知道其所有网络操作何时完成,您可以将字符串列表保存在可变数组中,例如“1 done”、“2 done”等,这与发送的字符串相同在 connectionDidFinishLoading: 中调用的通知的用户信息中。所有服务都可以发送相同的通知,但用户信息不同。在观察者的选择器中,删除与用户信息中的字符串相同的字符串,并检查数组是否为空——如果为空,则所有服务都已完成。那时,我会使用委托方法将数据传回视图控制器。在 Loader 中是这样的:

- (void)viewDidLoad 
    [super viewDidLoad];
    self.doneStrings = [@[@"1 done", @"2 done", @"3 done", @"4 done"] mutableCopy];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:@"SeriveFinishedNotification" object:nil];


-(void)notificationReceived:(NSNotification *) aNote 
    [self.doneStrings removeObjectIdenticalTo:[aNote.userInfo objectForKey:@"doneString"]];
    if (self.doneStrings.count == 0)
        [delegate doSomethingWithTheData: theData];

您可能需要做一些其他事情,例如处理某些网络操作失败的情况。

【讨论】:

【参考方案2】:

如果您想等到异步任务完成,您可以使用信号量。看下面的例子,逻辑很简单。我认为您可以轻松适应您的情况。

//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) 

      //some code here executed in background

        dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore

    failure:^(AFHTTPRequestOperation *operation, NSError *error) 

       //some other code here also executed in background

        dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
    ];


//holds the thread until the dispatch_semaphore_signal(semaphore) is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];

【讨论】:

【参考方案3】:

您尚未分享这些异步请求如何工作的详细信息,但另一种方法是将这些异步请求 NSOperation 对象提交给 NSOperationQueue。 (AFNetworking 是此类实现的一个示例。)当您这样做时,您可以创建另一个NSOperation,以便在网络请求操作完成时触发,方法是使其依赖于这些网络请求操作。因此,它只会在所有网络请求都完成后运行。使用基于NSOperation 的解决方案还有其他好处(例如,您可以使用setMaxConcurrentOperationCount 让您享受并发,但在任何给定时间都不会运行太多并发请求)。

参考文献

Ray Wenderlich 的How To Use NSOperations and NSOperationQueues Defining a Custom Operation Object 在 Apple 的并发编程指南

【讨论】:

谢谢。我对我选择的方法比使用 NSOperation 更熟悉一些,但是如果使用 NSOperationQueue 可以在这样的场景中提供好处,我会花时间关注你的参考 @AppsDev 虽然子类化操作是一种非常有用的技术,可以让您在某些时候熟悉它,如果您不想被所涉及的微妙问题所困扰,您也可以查看AFNetworking,一个备受推崇的第三方网络库,它 (a) 为您执行此操作; (b) 通常比自己编写网络编程更容易。 在回答好处的问题时,我在回答中提到的关键优势之一就是能够使用具有 5 个并发操作的队列,因此您可以将一堆请求排队,操作队列不仅负责并发性,还负责调度它们,因此您在任何给定时间都不会运行太多。它还为取消操作提供了很好的机制。在例如 tableview 中检索图像色调时非常有用。或doing a lot of downloads.

以上是关于在 iOS 中等待多个网络异步调用的主要内容,如果未能解决你的问题,请参考以下文章

在 XCTestCase 的 setUp() 中等待多个异步调用

有没有办法在异步函数中等待多个等待调用?

等待多个异步调用完成?

同时等待多个具有独立延续的 WCF 异步调用

如何等待来自 forEach 循环的多个异步调用?

如何等待来自 for 循环的多个异步调用?