取消完成块

时间:2016-04-29 09:24:58

标签: ios swift closures block

快速提问。

通常,我们进行Web调用并获取响应,我们将在完成块中返回数据,如下所示

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

我使用下面的功能

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

是否可以在以后的某个时间以某种方式取消完成块?比如,如果我立即进行网络呼叫并popViewController,但如果我取消request,如果数据返回到完成区,则执行完成任务。

我是否有任何机制可以将var分配给关闭,并可以在以后取消?

如果我需要,我怎么能取消阻止执行,比如viewWillDisappear

4 个答案:

答案 0 :(得分:5)

你不一定要删除存在的完成块,但由于它是你的完成块,你可以很容易地在调用时不做任何事情:

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

somethingOrOther可能是self的属性,或者(正如您已经被告知的那样)您可能会检查self是否仍然存在。

这与NSOperation使用的机制没有什么不同,NSOperation可以在实际执行任何操作之前检查自己的cancelled属性。

答案 1 :(得分:2)

我的猜测是你在完成块中强烈保留自我。如果在捕获列表中传递对self的弱引用,则在释放视图控制器时将不会执行您的操作。

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

    strongSelf.label.text = ...
}

请注意,这仅适用于您在块中执行的任务是在self上执行的情况。 someAPIcall仍然保持对完成块的引用,但是完成块对视图控制器的引用很弱。 (从技术上讲,你可以使用弱自我的值来检查是否执行其他任务)。

如果这还不够,您可以访问someAPIcall的实现,那么您可以添加cancel()方法(正如其他人提到的那样)而不是停止调用,并释放阻止。

答案 2 :(得分:1)

没有什么可以让你直接取消任何阻止。因此块将在调用时执行。但是,块当然可以执行代码,以确定是否仍然需要执行它应该执行的任何操作。

通常,您只能对块中的某个对象进行弱引用,如果该弱引用为零,则不执行操作。在其他情况下,您将检查对象的某些属性。总是有效的简单方法:使用单个实例属性创建一个简单的类"取消"。创建并获取一个实例,让块引用它,当你想取消块设置"取消" property为true,回调检查该设置。 (同样,你可以把它作为一个弱引用,如果一个调用者不再感兴趣,他们就可以放开那个实例)。

答案 3 :(得分: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调用。

当然这是一个相当简单的例子,但是您明白了 - 了解您的应用程序正在进行的网络调用量是否很好,如果不再需要它们就取消它们!