5

Ich muss eine Reihe von Informationen von meinem RestAPI synchronisieren. Ich muss 6 RestAPI-Aufrufe ausführen, um die Arbeit abzuschließen. Ich entwarf API-Aufrufe mit Blöcken und gab NSError zurück, wenn es welche gibt. 3 dieser Aufrufe sollten verschachtelt ausgeführt werden, da der erste Aufruf Informationen an andere weitergibt und die Ausführung ermöglicht, während die anderen 3 Aufrufe unabhängig voneinander ausgeführt werden können. Durch die Netzwerk-Performance zu verbessern, entwarf ich meinen Synchronisationsaufruf wie folgt:NSBlockOperation, NSOperationQueue und Blöcke

  • 1 NSBlockOperation, die die ersten verschachtelten 3 Blöcke enthalten;
  • 1 NSBlockOperation, die andere drei Blöcke enthält;
  • 1 NSBlockOperation, die ich als "semphore" benutze und sagt mir, wenn alle Arbeit erledigt ist.

Die letzte NSBlockOperation hat eine Abhängigkeit von den vorherigen zwei NSBlockOperation.

Ich habe auch eine NSOperationQueue, die alle drei NSBlockOperation enthält, wo der Semaphor NSBlockOperation als letzte in der Warteschlange hinzugefügt wird. Das Ergebnis, das ich erreichen würde, ist: erstens zwei Blöcke, die gleichzeitig aufgerufen werden, und wenn ihre Arbeit beendet ist, wird der Semaphor NSBlockOperation aufgerufen und gibt die Steuerelemente an den Benutzer zurück, der UIAlertMessage bereitstellt.

Das Ergebnis ist, daß nicht zuvor erläutert: Kontrollen werden zurückgegeben, ohne das Ende des syncAllBlocksInformation Block wartet.

Unter dem Code, der NSBlockOperation enthält:

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

} 

Und hier Code, der über Block ruft:

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

}]; 
} 

Was ist los?

+0

Haben Sie versucht, 'NSOperationQueue' eine Instanzvariable der einschließenden Klasse zu machen? –

+0

tat ich. Ich habe es als Instanzvariable deklariert und in ViewDidLoad instanziiert. Nichts hat sich geändert. – dpizzuto

Antwort

3

Das Problem ist, dass NSBlockOperation für synchrone Blöcke ist. Es wird finished sein, sobald seine Blöcke fertig sind. Wenn seine Blöcke asynchrone Methoden auslösen, werden diese unabhängig ausgeführt.

Zum Beispiel, wenn Ihr syncUserInfoOperation ‚s Block ausgeführt wird, löst es [dataSync syncUserInfo:...] aus und dann sieht sich getan; es wartet nicht auf einen der Komplettierungshandler, oder irgendetwas ähnliches.

Eine gute Lösung hierfür ist die Erstellung eigener NSOperation Unterklassen. Wahrscheinlich möchten Sie für jeden Ihrer Datensynchronisierungstypen einen erstellen, um die Einrichtung von Abhängigkeiten usw. zu erleichtern, aber das liegt an Ihnen. Sie können alles darüber lesen, wie man das macht here (lesen Sie unbedingt den Abschnitt auf "Configuring Operations for Concurrent Execution").

Sie könnten auch eine generische NSOperation Unterklasse erstellen, die einen Block akzeptiert, der asynchron ausgeführt werden kann. Das Hauptproblem dabei ist, dass es viel schwieriger ist, Dinge wie das Abbrechen der Operation zu erledigen, die Sie wahrscheinlich immer noch wollen.

+0

Ich fand heraus, dass nur eine mögliche Lösung darin besteht, NSOperation abzuleiten und manuell zwei Arten von asynchronen Blöcken zu verwalten, aber ich dachte, es gäbe eine andere "leichte" Lösung. Ich schreibe Ihre Antwort als gültig an und werde baldmöglichst meine Implementierung veröffentlichen. Thx – dpizzuto

+0

Eine "leichtere" Lösung könnte die Verwendung der [Dispatch-Warteschlangen von Grand Central Dispatch] sein (https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1) ("NSOperation" und Familie verwenden diese in ihrer Implementierung). Zusammen mit den Funktionen zum Erstellen von Warteschlangen und zum Senden an diese sollten Sie auch Dispatch-Gruppen für Ihr Abhängigkeitsmanagement anzeigen. Versandwarteschlangen sind sehr leistungsfähig und leicht, aber Sie würden einige nette Funktionen verlieren, die in 'NSOperation' eingebaut sind. –