2014-06-25 12 views
11

Das neue Cloud Kit-Framework verwendet NSOperation umfassend für seine CRUD. Die Ergebnisse dieser Operationen werden in Blöcken zurückgegeben. Zum Beispiel:Wie kommuniziert man Ergebnisse zwischen NSOperation-Abhängigkeiten?

let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2]) 

fetchOperation.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in 
      // dict contains RecordId -> Record    
      // do something with the records here (if no error) 
     } 

ich Kette wollen einige dieser Operationen (Abhängigkeiten) und das Ergebnis einer Operation zur nächsten Operation in der Kette übergeben. Vereinfachtes Beispiel zu veranschaulichen diesen (Pseudo-Code!):

let fetchOperation1 = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2]) 

fetchOperation1.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in 
      if error { 
       // handle error 
      } else { 
       // dict contains RecordId -> Record    
       // let's pretend our records contain references to other records 
       // that we want to fetch as well 
       fetchOperation.operationResult = 
        dict.allValues().map(
         { $0.getObject("referencedRecordId"} 
       ) 
      } 
     } 

let fetchOperation2 = CKFetchRecordsOperation(recordIDs: fetchOperation1.operationResult) 

fetchOperation2.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in 
      if error { 
       // handle error 
      } else { 
       // dosomething 
      } 
     } 

fetchOperation2.addDependency(fetchOperation2) 

Aber vor Pseudo-Code nie funktionieren kann, da die fetchOperation1.operationResult ist noch nicht zugewiesen, wenn Sie fetchOperation2 init. Sie könnten die init von fetchOperation2 in completionBlock von fetchOperation1 verschachteln, aber dann verlassen Sie die Abhängigkeitsfunktion von NSOperation, die ich hier verwenden möchte.

Also, ich bin auf der Suche nach einer sauberen, lesbaren, Standard (keine reaktiven Kakao und so) Lösung zu arbeiten haben NSOperation Abhängigkeiten Daten entlang in ihrer Kette weitergeben.

+0

Wenn Sie die zweite Operation in der ersten Operation der Beendigung Block, die zweite Operation nach dem ersten immer ausgeführt werden erstellen. Warum ist es wichtig, ob Sie Abhängigkeiten zuweisen oder nicht, wenn Sie immer noch in der Reihenfolge ausgeführt werden? –

+0

@ TomHarrington Dies ist ein vereinfachtes Beispiel. Ich habe eine Operation A, die ihr Ergebnis an Operation B1 und Operation B2 sendet, die ihr Ergebnis an Operation C senden müssen. Das Verschachteln wird ziemlich schnell zu einem Chaos (aka 'Callback-Hölle') und benötigt ein Muster, um es für 'komplex lesbar zu machen 'Fälle wie diese. –

Antwort

6

Ich erinnere mich, als NSOperation zum ersten Mal eingeführt wurde, und ich einen einleitenden Artikel für die ADC-Site schreiben musste, die zuerst einige Fotos herunterladen und dann in ein Poster rendern würde. Ich stieß dort auf ähnliche Probleme: Verwenden von Abhängigkeiten, um die Reihenfolge zu kontrollieren, aber dann musste ich die Bilddateinamen an die abhängige Operation übergeben.

Das war vor vielen Jahren, und zu dieser Zeit hatte ich NSOperation Unterklassen für jede Aufgabe. Ich setzte Abhängigkeiten zwischen ihnen und fügte einen Delegaten zu den Operationen hinzu, die Ergebnisse von einer früheren Operation übergeben werden mussten. In der Delegate-Methode würde das Controller-Objekt die Ergebnisse einer Eigenschaft der ersten Operation abrufen und sie über eine Eigenschaft für die zweite Operation festlegen.

Eine bessere Lösung besteht vielleicht darin, die Beziehungen zwischen Operationen explizit zu machen, nicht nur in Bezug auf Abhängigkeiten, sondern auch in Bezug auf die Weitergabe von Daten. So könnten Sie NSOperation Unterklassen erstellen, die Daten als Teil der Standardoperation an die nächste NSOperation übergeben, oder — eleganter — ziehen Daten aus einer abgeschlossenen Operation.

Um das konkreter zu machen: Operation B hängt davon ab, dass A abgeschlossen ist. A erzeugt die Ressource R, die von B zum Ausführen benötigt wird. Sie fügen B eine Eigenschaft hinzu, die auf das A-Objekt verweist. Wenn B ausgeführt wird, ruft es einfach R von dem A-Objekt ab.

Wenn Sie lieber nicht den Betrieb Subklassen erstellen, und wollen einfach nur Verschachtelung von Blöcken zu vermeiden, können Sie einen Warteschlangenmechanismus berücksichtigen, die Sie ein bisschen mehr Kontrolle, wie CDEAsynchronousTaskQueue gibt.

+0

WWDC 2015 Advanced NSOperations hat diesbezüglich eine Menge zu bieten. – Andy

+0

Leider nicht. Das einzige Beispiel, um Daten dort zu teilen, wird unter Verwendung der temporären Cache-Datei implementiert, um Daten zwischen dem Netzwerkbetrieb und dem Parsing-Vorgang freizugeben. Es ist wie ein versteckter globaler Singleton – Mindaugas

2

erklären Sie einfach die zweite Operation über dem ersten Block, so dass Sie die RecordIDs einstellen können auf sie holen, wie bei dieser Bearbeitung auf Ihr Beispiel demonstriert:

let fetchOperation1 = CKFetchRecordsOperation(recordIDs: [recordID1, recordId2]) 
let fetchOperation2 = CKFetchRecordsOperation() 

fetchOperation1.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in 
      if error { 
       // handle error 
      } else { 
       // dict contains RecordId -> Record    
       // let's pretend our records contain references to other records 
       // that we want to fetch as well 
       fetchOperation2.recordIDs = 
        dict.allValues().map(
         { $0.getObject("referencedRecordId"} 
       ) 
      } 
     } 

fetchOperation2.fetchRecordsCompletionBlock = {(dict: NSDictionary!, error: NSError!) -> Void in 
      if error { 
       // handle error 
      } else { 
       // dosomething 
      } 
     } 

fetchOperation2.addDependency(fetchOperation1) 

Auch wenn Sie einen Fehler in dem ersten Block erhalten , sollten Sie alle Operationen in der Warteschlange abbrechen. Jeder Block wird immer noch aufgerufen, aber NSError wird auf abgebrochen gesetzt. Sie benötigen also keinen speziellen Endblock wie bei benutzerdefinierten Operationen.