2016-04-29 10 views
4

クイック質問。完了ブロックをキャンセルします。

通常、我々はウェブ呼び出しを行い、応答を取得するには、我々は

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

以下のように完了ブロックにデータを返すと私は

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

以下としての機能の使用がそれであることを確認してください何らかの形で完了ブロックを取り消すことができますか?さて、もし私がを即座に呼び出しても、requestを取り消しても、データが完了ブロックに返されれば完了タスクが実行されます。

閉鎖にvarを割り当てることができたメカニズムはありますか?それを後でキャンセルできますか?

viewWillDisappearのように、必要なときにブロックの実行をキャンセルするにはどうすればよいですか?

+0

どのように完了ブロックを保持し、活動をキャンセルしていますか? – Wain

+0

私はそうすることが可能です..完了クロージャの活動をキャンセルするには... – iOS

+0

完了ブロックをキャンセルしないで完了ブロックを呼び出す必要があります... – Wain

答えて

1

を直接でブロックすることはできません。ブロックは、呼び出されるとが実行されます。ただし、ブロックは、実行する必要があるアクションがまだ必要かどうかを判断するコードを実行できます。

弱い参照がnilの場合は、ブロック内のオブジェクトに弱い参照があってもアクションを実行しないことがよくあります。他の場合、オブジェクトのプロパティをチェックします。常に機能する簡単なメソッド:「キャンセル」された単一のインスタンスプロパティを持つ簡単なクラスを作成します。インスタンスを作成して取得し、ブロックを参照させます。ブロックをキャンセルする場合は、「キャンセル」プロパティをtrueに設定し、コールバックはその設定をチェックします。 (また、弱い参照にすることもできます。発信者がそれ以上興味がない場合は、そのインスタンスを放棄できます)。

2

私はあなたが完全に完了ブロックに自分自身を保持していると思います。キャプチャリストで弱い参照をselfに渡すと、ビューコントローラが解放されてもアクションは実行されません。

someAPIcall(input) { [weak self] result in 
    guard let strongSelf = self else { return } 

    strongSelf.label.text = ... 
} 

これは、ブロックで実行しているタスクが自己で実行されている場合にのみ有効です。 someAPIcallはまだコンプリートブロックへの参照を保持していますが、コンプリートブロックにはビューコントローラに対する弱い参照があります。 (技術的には、弱い自己の価値を使って他の仕事をするかどうかをチェックすることができます)。

これは、十分ではありません場合はsomeAPIcallの実装へのアクセスを持って通話を停止し、ブロックを解放するよりも、あなたは(他の人が言及したように)cancel()メソッドを追加することができます。

5

あなたは必ずしも存在から完了ブロックを消去することはできませんが、それはあなたの完了のブロックなので、それが呼び出されたとき、あなたは簡単にだけで何もできない。

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

somethingOrOtherselfの財産であるかもしれません、または(既に伝えられているように)selfがまだ存在するかどうかを確認するかもしれません。

これは、実際に何かを実行する前に独自のcancelledプロパティをチェックできるNSOperationで使用されるメカニズムとあまり変わりません。

0

これにはさまざまな方法があります。適切な答えは、特定のユースケースと、APIのやりとりを設計した方法によって異なります。 NSOperationsは大きなキャンセル/依存関係の管理/完了ワークフローを持っていますので、APIインタラクションをNSOperationQueueに配置することができれば最善の方法です。私がより単純なやりとりのために使う別の可能性は、ビューコントローラの特定のビューインタラクションに対応するNSURLSessionTasksへの参照を単純に保持し、必要に応じて取り消すことです。たとえば、次のように

ここ
//: 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 
    } 
} 

、ビューコントローラの割り当てが解除されたとき、私たちはこのコントローラに関連するすべてのNSURLSessionTasksをキャンセルします。また、ユーザーが検索バーに入力する際に​​既存の「検索」APIタスクをキャンセルし、「古い」API呼び出しをしないようにします。

これはかなり簡単な例ですが、アプリケーションが作成しているネットワークコールの量については賢明であり、もはや必要がなくなったら取り消すのは良い考えです。

関連する問題