5

Devo sincronizzare un gruppo di informazioni dal mio RestAPI. Devo fare 6 chiamate RestAPI per completare il lavoro. Ho progettato le chiamate API con Blocks e restituisco NSError se ce n'è. 3 di queste chiamate devono essere eseguite nidificate perché la prima chiamata fornisce informazioni agli altri e consente l'esecuzione mentre le altre 3 chiamate possono essere eseguite indipendentemente. causa di migliorare le prestazioni della rete, ho progettato la mia chiamata di sincronizzazione come segue:NSBlockOperation, NSOperationQueue e Blocks

  • 1 NSBlockOperation che contiene i primi 3 blocchi annidati;
  • 1 NSBlockOperation che contiene altri tre blocchi;
  • 1 NSBlockOperation che uso come "semphore" e mi dice quando tutto il lavoro è finito.

Ultimo NSBlockOperation ha dipendenza dai precedenti due NSBlockOperation.

Ho anche un NSOperationQueue che contiene tutti e tre gli NSBlockOperation in cui il semaforo NSBlockOperation viene aggiunto come ultimo nella coda. Il risultato che dovrei ottenere è: primi due blocchi chiamati simultaneamente e quando il loro lavoro termina, viene chiamato il semaforo NSBlockOperation e restituisce i controlli all'utente che fornisce UIAlertMessage.

Il risultato non è quello spiegato in precedenza: i controlli vengono restituiti senza attendere la fine del blocco syncAllBlocksInformation.

Sotto il codice che 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]; 

} 

E qui, codice che chiama sopra blocco:

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

}]; 
} 

Cosa c'è di sbagliato?

+0

Hai provato a rendere 'NSOperationQueue' una variabile di istanza della classe che include? –

+0

L'ho fatto. Ho dichiarato come variabile di istanza e l'ho istanziato in ViewDidLoad. Niente è cambiato. – dpizzuto

risposta

3

Il problema è che NSBlockOperation è per blocchi sincroni. Sarà finished non appena il blocco (i) avrà completato l'esecuzione. Se i relativi blocchi attivano metodi asincroni, quelli verranno eseguiti indipendentemente.

Ad esempio, quando viene eseguito il blocco di syncUserInfoOperation, esso si spegne [dataSync syncUserInfo:...] e quindi si considera eseguito; non aspetta che qualcuno degli addetti al completamento sparino, o qualcosa del genere.

Una buona soluzione a questo è creare le proprie sottoclassi NSOperation. Probabilmente vorrai crearne uno per ciascuno dei tuoi tipi di sincronizzazione dei dati per semplificare l'impostazione delle dipendenze, ecc., Ma dipende da te. Puoi leggere tutto su come farlo here (assicurati di leggere la sezione "Configuring Operations for Concurrent Execution").

È anche possibile creare una sottoclasse generica NSOperation che accetta un blocco che può essere eseguito in modo asincrono. Il problema principale è che rende molto più difficile gestire cose come la cancellazione dell'operazione, che probabilmente vorresti comunque.

+0

Ho capito che solo la soluzione possibile è la sottoclasse di NSOperation e gestire manualmente due tipi di blocco asincrono, ma ho pensato che ci fosse un'altra soluzione "leggera". Contrassegno come valida la tua risposta e nel più breve tempo possibile posterò la mia implementazione. Thx – dpizzuto

+0

Una soluzione "più leggera" potrebbe essere quella di utilizzare [le code di invio di Grand Central Dispatch] (https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1) ('NSOperation' e famiglia usano quelli nella loro implementazione). Insieme alle funzioni per creare code e inviare a loro, dovresti anche esaminare i gruppi di spedizione per eseguire la gestione delle dipendenze. Le code di invio sono molto potenti e leggere, ma si perderebbero alcune funzionalità integrate in 'NSOperation'. –