我有一个包含三个异步方法的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];
}
你有任何技术可以得到这个结果吗? 非常感谢你。
答案 0 :(得分:14)
一种GCD方法是使用dispatch_group
。因此,在启动异步任务之前,请调用dispatch_group_enter
,然后在异步任务完成时调用dispatch_group_leave
,然后创建一个dispatch_group_notify
,当异步任务将被调用完。你可以将它与完成块模式结合起来(无论如何,这对于异步方法是一个好主意):
如果getInformations
,getExperiences
和getEducation
本身就是所有异步方法,那么首先需要的是一些了解它们何时完成的机制。一种常见的解决方案是为每个实现完成块模式。例如:
// 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();
}
}];
}
也为getExperiences
和getEducation
重复此过程。
然后,您可以使用调度组通知您这三个请求中的每个请求何时完成,并在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();
}
});
}
然后您在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];
});
}
最后两个想法:
概述了所有这些,我必须承认我可能会将这些请求包装在并发/异步自定义NSOperation
子类中,而不是使用调度组。请参阅Concurrency Programming Guide的“配置并发执行操作”部分。这是对代码的更彻底的重构,所以我不会在这里解决,但是它允许你限制将同时运行的这些请求的数量,减少潜在的超时问题。
我不知道有多少这些用户请求正在进行,但您可能需要考虑在用户信息进入时更新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.
});
});