2014-10-13 18 views
6

Ho un ciclo for che contiene tre metodi asincroni e voglio fare un po 'di trattamento dopo che sono finiti i 3 metodi asincroni.Attendi che i metodi asincroni finiscano in un ciclo for

-(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]; 
} 

Avete qualche tecnica per avere questo risultato? Grazie mille.

+0

sono 'getInformations',' 'getEducation', essi stessi, i metodi asincroni getExperiences' e? O sono tutti metodi sincroni che spediresti in modo asincrono a qualche coda in background? (La tua modifica alla domanda cambia radicalmente la risposta.) – Rob

+0

@Rob; Sì, sono asincroni! 'getAllUsersInformations'' getInformations'' getExperiences' e' getEducation' sono asincroni. – androniennn

+0

@androniennn puoi rendere sincroni i tre metodi '-getXXX'? –

risposta

14

Un approccio GCD è quello di utilizzare dispatch_group. Quindi, prima di avviare un'attività asincrona, chiamare dispatch_group_enter e, al termine dell'attività asincrona, chiamare dispatch_group_leave e creare quindi un dispatch_group_notify che verrà chiamato al termine delle attività asincrone. Si può sposare questa con un motivo a blocchi di completamento (che è una buona idea per i metodi asincroni, in ogni caso):

  1. Se getInformations, getExperiences e getEducation sono, essi stessi, tutti i metodi asincroni, la prima cosa che serve è un po ' meccanismo per sapere quando hanno finito. Una soluzione comune consiste nell'implementare un modello di blocco di completamento per ciascuno. Per esempio:

    // 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(); 
         } 
        }]; 
    } 
    

    Ripetere questa procedura per getExperiences e getEducation, anche.

  2. Quindi, è possibile utilizzare un gruppo di spedizione per informare l'utente di quando ognuna di queste tre richieste sono fatto fare, chiamare un blocco di completamento nel getUserInfo quando ciò avviene:

    // 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. E poi ripetere tale processo a 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]; 
        }); 
    } 
    

Due considerazioni finali:

  1. aver ripercorso tutto questo, devo confessare che avrei probabilmente avvolgere queste richieste in costume concorrente/asincrona NSOperation sottoclassi invece di utilizzare gruppi di spedizione. Vedere la sezione "Configurazione delle operazioni per l'esecuzione simultanea" di Concurrency Programming Guide. Questo è un refactoring più radicale del codice, quindi non lo affronterò qui, ma ti permetterà di limitare il numero di queste richieste che verranno eseguite contemporaneamente, mitigando potenziali problemi di timeout.

  2. Non so quante di queste richieste utente stiano accadendo, ma potresti voler considerare l'aggiornamento dell'interfaccia utente quando arrivano le informazioni dell'utente, piuttosto che aspettare che tutto finisca. Questo è, ancora una volta, un refactoring più radicale del codice, ma potrebbe portare a qualcosa che si sente più reattivo.

+0

Che spiegazione! Ho capito la tecnica e cercherò di implementarla. Lascerò un feedback;). – androniennn

+0

l'ultimo 'group_notify' che ricarica la tableview non viene mai chiamato. Non so perché specialmente come viene chiamato 'group_leave' di' getUserInfo'. – androniennn

+0

Prima di tutto, assicurati che la notifica finale del gruppo non sia stata chiamata (e che non sia solo un problema che la ricarica della tabella non abbia funzionato correttamente). In secondo luogo, per diagnosticare ciò, registrare il gruppo enter e lasciare in 'getAllUsersInformations' e assicurarsi che siano stati tutti chiamati. – Rob

-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. 
    }); 
}); 
+0

I tre metodi in '-getUserInfo:' sono asincroni, non lo faranno tornare presto? –

+0

@AaronA. : Esattamente Aaron. – androniennn

+0

No, quei metodi verranno chiamati in modo sincrono sullo sfondo. In effetti se metterai un breakpoint entrando nel blocco lo passerà sopra. Terminato il ciclo for, il blocco 'dispatch_async (dispatch_get_main_queue(),^{ // ricarica tableview, questo è sul thread principale. });' sarà chiamato sul thread principale. Fare un test rapido dovrebbe funzionare. –

0

Provare a fare un blocco con il completamento, non è possibile farlo con un ciclo for se i metodi sono asincroni. devi chiamare getUserInfo uno per uno dopo il completamento del precedente. Penso che questo risolverà il tuo problema.

-(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]; 
} 

Spero che questo ti possa aiutare.

0

Rop risposta con rapida:

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 
     }) 
}