等待异步方法在for循环中完成

时间:2014-10-13 15:17:09

标签: ios grand-central-dispatch

我有一个包含三个异步方法的for循环,我想在这3个异步方法完成后进行一些处理。

 -(void)getAllUsersInformations{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           for(User *user in users){
              [self getUserInfo:user];
           }
      //Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods).
    });
}

-(void)getUserInfo:(User*)user{
     [self getInformations:user];
     [self getExperiences:user];
     [self getEducation:user];
}

你有任何技术可以得到这个结果吗? 非常感谢你。

4 个答案:

答案 0 :(得分:14)

一种GCD方法是使用dispatch_group。因此,在启动异步任务之前,请调用dispatch_group_enter,然后在异步任务完成时调用dispatch_group_leave,然后创建一个dispatch_group_notify,当异步任务将被调用完。你可以将它与完成块模式结合起来(无论如何,这对于异步方法是一个好主意):

  1. 如果getInformationsgetExperiencesgetEducation本身就是所有异步方法,那么首先需要的是一些了解它们何时完成的机制。一种常见的解决方案是为每个实现完成块模式。例如:

    // added completionHandler parameter which will be called when the retrieval
    // of the "informations" is done.
    
    - (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler {
        // do whatever you were before, but in the asynchronous task's completion block, call this
        // completionHandler()
        //
        // for example
    
        NSURLRequest *request;
    
        [NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            // handle the request here
    
            // the important thing is that the completion handler should
            // be called _inside_ the this block
    
            if (completionHandler) {
                completionHandler();
            }
        }];
    }
    

    也为getExperiencesgetEducation重复此过程。

  2. 然后,您可以使用调度组通知您这三个请求中的每个请求何时完成,并在getUserInfo中调用完成块:

    // added completion handler that will be called only when `getInformations`,
    // `getExperiences` and `getEducation` are all done.
    //
    // this takes advantage of the completion block we added to those three
    // methods above
    
    - (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler {
        dispatch_group_t group = dispatch_group_create();
    
        // start the three requests
    
        dispatch_group_enter(group);
        [self getInformations:user completionHandler:^{
            dispatch_group_leave(group);
        }];
    
        dispatch_group_enter(group);
        [self getExperiences:user completionHandler:^{
            dispatch_group_leave(group);
        }];
    
        dispatch_group_enter(group);
        [self getEducation:user completionHandler:^{
            dispatch_group_leave(group);
        }];
    
        // this block will be called asynchronously only when the above three are done
    
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            if (completionHandler) {
                completionHandler();
            }
        });
    }
    
  3. 然后您在getAllUsersInformations

    重复此过程
    // call new getUserInfo, using dispatch group to keep track of whether
    // all the requests are done
    
    -(void)getAllUsersInformations {
    
        dispatch_group_t group = dispatch_group_create();
    
        for(User *user in users){
            dispatch_group_enter(group);
    
            [self getUserInfo:user completionHandler:^{
                dispatch_group_leave(group);
            }];
        }
    
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });
    }
    

  4. 最后两个想法:

    1. 概述了所有这些,我必须承认我可能会将这些请求包装在并发/异步自定义NSOperation子类中,而不是使用调度组。请参阅Concurrency Programming Guide的“配置并发执行操作”部分。这是对代码的更彻底的重构,所以我不会在这里解决,但是它允许你限制将同时运行的这些请求的数量,减少潜在的超时问题。

    2. 我不知道有多少这些用户请求正在进行,但您可能需要考虑在用户信息进入时更新UI,而不是等待所有内容完成。这又是对代码进行更彻底的重构,但可能会产生更具响应性的内容。

答案 1 :(得分:0)

尝试在完成时执行一个块,如果方法是异步的,则不能使用for循环执行此操作。你必须在完成前一个之后逐个调用getUserInfo。我想这会解决你的问题。

  -(void)getAllUsersInformations{
    [self registerUserAtIndex:0];
  }

  - (void) registerUserAtIndex: (NSInteger ) userIndex
  {
  RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]];
  [RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) {
      dispatch_async(dispatch_get_main_queue(), ^{
      if (userIndex++ < [users count] {
         [self registerUserAtIndex:userIndex++];
      } else {
         [myTableView reloadData];
      }
   }];
  [[NSOperationQueue mainQueue] addOperation:op];
 }

希望这会对你有所帮助。

答案 2 :(得分:0)

Rop用 swift 答案:

func processData()
{
    let group: dispatch_group_t  = dispatch_group_create()

    for item in data as! Object {
        dispatch_group_enter(group)
        item.process(completion: {() -> (Void) in
          dispatch_group_leave(group)
        })
      }

    dispatch_group_notify(group, dispatch_get_main_queue(), {
        //Do whatever you want
      })
}

答案 3 :(得分:-1)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Background work
       for(User *user in users){
          [self getUserInfo:user];
       }

    dispatch_async(dispatch_get_main_queue(), ^{
     //reload tableview , this is on main thread.
    });
});