2016-04-29 14 views
4

Schnelle Frage.Einen Abschlussblock abbrechen

Normalerweise machen wir Anruf Web-und-Antwort auf den Erhalt kehren wir die Daten in einem Abschluss-Block wie unten

func someAPIcall(input: input, completion: (result: data) -> Void) { 
    ... 
    completion(data) 
} 

und ich Nutzung der Funktion machen, wie unten

someAPIcall(input) { 
    (result: data) in 
    print(result) 
    // Update UI, etc 
} 

es ist möglich, den Abschlussblock irgendwie zu irgendeinem späteren Zeitpunkt zu löschen? Sagen wir, wenn ich den Internetanruf mache und popViewController sofort, obwohl, wenn ich die request abbringe, wenn die Daten zum Abschlussblock zurückgebracht werden, wird der Abschlussauftrag ausgeführt.

Gibt es einen Mechanismus, mit dem ich dem Verschluss eine var zuweisen und das später aufheben könnte?

Wie kann ich die Ausführung des Blocks abbrechen, wenn ich es benötige, sagen wir in viewWillDisappear?

+0

Wie behalten Sie den Abschlussblock bei und stornieren die Aktivität? – Wain

+0

Ich frage, ist es möglich, dies zu tun .. Um die Beendigung Schließung Aktivitäten zu stornieren .. – iOS

+0

nicht den Abschluss Block abbrechen, sobald es läuft, müssen Sie nicht den Abschlussblock ... – Wain

Antwort

1

Es gibt nichts, das Sie direkt jeden Block abbrechen können. So wird der Block ausführen, wenn es aufgerufen wird. Der Block kann natürlich Code ausführen, der herausfindet, ob die Aktion, die er ausführen soll, noch benötigt wird.

Oft hätten Sie nur eine schwache Referenz auf ein Objekt in Ihrem Block und würden keine Aktion ausführen, wenn diese schwache Referenz gleich Null ist. In anderen Fällen würden Sie eine Eigenschaft eines Objekts überprüfen. Eine einfache Methode, die immer funktioniert: Erstellen Sie eine triviale Klasse mit einer einzigen Instanzeigenschaft "cancelled". Erstellen und erhalten Sie eine Instanz, lassen Sie den Block darauf verweisen, wenn Sie den Block abbrechen möchten, setzen Sie die Eigenschaft "cancelled" auf "true", und der Callback prüft diese Einstellung. (Wiederum könnten Sie es zu einer schwachen Referenz machen, und wenn ein Anrufer nicht mehr interessiert ist, kann er diese Instanz einfach loslassen).

2

Meine Vermutung ist, dass Sie Selbst im Abschlussblock stark beibehalten. Wenn Sie in der Erfassungsliste einen schwachen Verweis auf self übergeben, werden Ihre Aktionen nicht ausgeführt, wenn der View-Controller freigegeben wird.

Beachten Sie, dass dies nur funktioniert, wenn die Aufgaben, die Sie im Block ausführen, auf self ausgeführt werden. someAPIcall behält immer noch einen Verweis auf den Abschlussblock bei, aber der Abschlussblock weist einen schwachen Verweis auf Ihren Ansichtscontroller auf. (Technisch könnten Sie den Wert von schwachem Selbst verwenden, um zu überprüfen, ob Sie Ihre anderen Aufgaben ausführen sollen).

Wenn dies nicht ausreicht, und Sie haben Zugriff auf die Implementierung von someAPIcall, dann können Sie eine cancel() Methode hinzufügen (wie andere erwähnt haben) als den Anruf beenden und den Block freigeben.

5

Sie können nicht unbedingt den von der Existenz Abschluss Block löschen, aber da es Ihr Abschluss Block ist, können Sie einfach nur nichts tun, wenn es aufgerufen wird:

func someAPIcall(input: input, completion: (result: data) -> Void) { 
    guard somethingOrOther else {return} 
    // ... 
    completion(data) 
} 

somethingOrOther könnte eine Eigenschaft von self sein, oder (wie dir schon gesagt wurde) könntest du prüfen, ob self überhaupt noch existiert.

Dies unterscheidet sich nicht wesentlich von dem von NSOperation verwendeten Mechanismus, der seine eigene cancelled-Eigenschaft überprüfen kann, bevor er überhaupt etwas unternimmt.

0

Es gibt viele verschiedene Möglichkeiten, dies zu tun. Die richtige Antwort hängt von Ihrem speziellen Anwendungsfall und der Art und Weise ab, wie Sie Ihre API-Interaktion gestaltet haben. NSOperations verfügen über einen hervorragenden Abbruch-/Abhängigkeitsmanagement-/Vervollständigungs-Workflow. Wenn Sie also Ihre API-Interaktionen in eine NSOperationQueue einfügen können, ist dies möglicherweise der beste Weg. Eine weitere Möglichkeit, die ich für einfachere Interaktionen verwende, besteht darin, einfach einen Verweis auf die NSURLSessionTasks beizubehalten, die einer bestimmten Ansichtsinteraktion des Ansichtscontrollers entsprechen, und sie nach Bedarf abzubrechen. Zum Beispiel:

//: Playground - noun: a place where people can play 

import UIKit 

class MyViewController: UIViewController, UISearchBarDelegate { 

    var tasks = [NSURLSessionTask]() 
    let client = MyAPIClient() 

    deinit { 
     cancelAllTasks() 
    } 

    func cancelAllTasks() { 
     tasks.forEach { $0.cancel() } 
    } 

    func cancelAllSearchTasks() { 
     tasks.filter({ $0.taskDescription == MyAPIClient.TaskDecription.search.rawValue }).forEach { $0.cancel() } 
    } 

    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { 
     // Cancel previous search as user types a new search 
     cancelAllSearchTasks() 

     guard let text = searchBar.text else { 
      return 
     } 

     tasks.append(client.search(text) { [weak self] results in 
      // ... 
     }) 
    } 

} 

class MyAPIClient { 

    enum TaskDecription: String { 
     case search 
    } 

    let session = NSURLSession() 

    func search(text: String, completion: (result: [String]) -> Void) -> NSURLSessionTask { 
     let components = NSURLComponents() 
     components.scheme = "http" 
     components.host = "myapi.com" 
     components.queryItems = [NSURLQueryItem(name: "q", value: text)] 
     guard let url = components.URL else { 
      preconditionFailure("invalid search url") 
     } 

     let task = session.dataTaskWithURL(url) { (data, response, error) in 
      // ... 
      completion(result: ["results", "from", "api", "go", "here!"]) 
     } 
     task.resume() 
     task.taskDescription = TaskDecription.search.rawValue 

     return task 
    } 
} 

Wenn hier die View-Controller freigegeben wird, wir löschen Sie alle NSURLSessionTasks, die auf diesem Controller verbunden sind. Wir löschen auch alle bestehenden "Such" -Api-Aufgaben, wenn der Benutzer in die Suchleiste eingibt, so dass wir keine "veralteten" API-Anrufe tätigen.

Natürlich ist dies ein ziemlich einfaches Beispiel, aber Sie bekommen die Idee - es ist gut, schlau zu sein über die Anzahl der Netzwerkanrufe, die Ihre Anwendung macht, und sie abzubrechen, wenn sie nicht mehr benötigt werden!