NSBlockOperation, NSOperationQueue y bloques

Tengo que sincronizar un montón de información de mi RestAPI. Debo hacer 6 llamadas RestAPI para completar el trabajo. Diseñé llamadas de API con bloques y devolví NSError si hay alguna. 3 de estas llamadas deben ejecutarse anidadas porque la primera llamada da información a otros y permite la ejecución, mientras que las otras 3 llamadas pueden ejecutarse de forma independiente. Debido a mejorar el performance de la networking, diseñé mi llamada de synchronization de la siguiente manera:

  • 1 NSBlockOperation que contiene los primeros 3 bloques nesteds;
  • 1 NSBlockOperation que contiene otros tres bloques;
  • 1 NSBlockOperation que uso como "semphore" y me dice cuándo todo el trabajo ha terminado.

El último NSBlockOperation tiene dependencia con dos NSBlockOperation anteriores.

También tengo un NSOperationQueue que contiene los tres NSBlockOperation donde se agrega el semáforo NSBlockOperation como último en la queue. El resultado que quiero lograr es: primero dos bloques llamados simultáneamente y cuando su trabajo finaliza, se llama al NSBlockOperation de semáforo y devuelve los controles al usuario que proporciona UIAlertMessage.

El resultado no es el explicado anteriormente: los controles se devuelven sin esperar el final del locking syncAllBlocksInformation .

Debajo del código que contiene NSBlockOperation:

-(void)syncAllBlocksInformation:(void(^)(NSError *error))completion{ __block NSError *blockError = nil; NSOperation *syncUserInfoOperation = [NSBlockOperation blockOperationWithBlock:^{ [dataSync syncUserInfo:tfMail.text password:tfPassword.text completion:^(NSError *error, NSNumber *idUser) { if(!error){ [dataSync syncUserfilesInfo:idUser completion:^(NSError *error) { if(!error){ [dataSync syncUserBookings:^(NSError *error) { if(error){ blockError = error; } }]; } else{ blockError = error; } }]; } else{ blockError = error; } }]; }]; NSBlockOperation *otherSyncOperations = [NSBlockOperation blockOperationWithBlock:^{ [dataSync syncNewsInfo:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; [otherSyncOperations addExecutionBlock:^{ [dataSync syncLocationsInfo:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; [otherSyncOperations addExecutionBlock:^{ [dataSync syncExoticAnimalTypesAndAnimals:^(NSError *error) { if(error){ blockError = error; NSLog(@"error %@",error); } }]; }]; NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"END"); }]; [completionOperation setCompletionBlock:^{ NSLog(@"Syc isEx %i",syncUserInfoOperation.isExecuting); NSLog(@"other isEx %i",otherSyncOperations.isExecuting); completion(blockError); }]; NSOperationQueue *opQueue = [NSOperationQueue new]; [completionOperation addDependency:syncUserInfoOperation]; [completionOperation addDependency:otherSyncOperations]; [opQueue addOperation:syncUserInfoOperation]; [opQueue addOperation:otherSyncOperations]; [opQueue addOperation:completionOperation]; } 

Y aquí, el código que llama arriba bloquea:

 -(IBAction)login:(id)sender{ [self dismissKeyboardOpened:nil]; hud=[MBProgressHUD showHUDAddedTo:self.view animated:YES]; [hud setLabelText:NSLocalizedString(@"login_hud_message", login_hud_message )]; [hud setMode:MBProgressHUDModeIndeterminate]; [self showHudAndNetworkActivity:YES]; [self syncAllBlocksInformation:^(NSError *error) { [self showHudAndNetworkActivity:NO]; if(!error){ NSLog(@"End LOGIN"); [self showAlert:@"Login" message:@"Login OK" dismiss:YES]; } else{ [self showAlert:@"Error" message:@"Login NO" dismiss:NO]; } }]; } 

¿Que pasa?

El problema es que NSBlockOperation es para bloques síncronos . Se finished tan pronto como su (s) bloque (s) haya terminado de ejecutarse. Si su (s) bloque (s) disparan methods asíncronos, esos se ejecutarán independientemente.

Por ejemplo, cuando se syncUserInfoOperation el bloque syncUserInfoOperation , dispara [dataSync syncUserInfo:...] y luego se considera hecho; no espera a que ninguno de los controlleres de finalización dispare, ni nada por el estilo.

Una buena solución para esto es crear sus propias subclasss NSOperation . Probablemente quiera crear uno para cada uno de sus types de synchronization de datos para facilitar la configuration de dependencies, etc., pero eso depende de usted. Puede leer todo sobre cómo hacerlo aquí (asegúrese de leer la sección sobre " Configuración de operaciones para la ejecución simultánea ").

También podría hacer una subclass NSOperation genérica que tome un bloque que se pueda ejecutar de forma asíncrona. El problema principal con esto es que hace que sea mucho más difícil manejar cosas como cancelar la operación, lo que probablemente aún desee.