@escaping关闭实际上在返回之前运行

时间:2018-04-02 16:25:23

标签: ios swift

我正在从远程源实现下载请求,我遇到了@escaping函数的概念。正如Apple所说:

  

当闭包被传递时,闭包被称为转义函数   函数的参数,但在函数返回后调用。

但我实际上注意到(使用断点工具)它在return语句之前调用和实现。

  static func fetchFeaturedApps(completionHandler: @escaping ([AppCategory]) -> ()) {
    let urlString = "https://api.letsbuildthatapp.com/appstore/featured"


    URLSession.shared.dataTask(with: URL(string: urlString)!) {
        (data, response,error) -> Void in

        if error != nil {
            print(error?.localizedDescription)
    return
        }
        do {
            let json = try(JSONSerialization.jsonObject(with: data!, options: .mutableContainers)) as! Dictionary<String, Any>
            var appCategories = [AppCategory]()
   // invokes before return [![enter image description here][1]][1]
            completionHandler(appCategories)

            for dict in json["categories"] as! [[String: Any]] {
                let appCategory = AppCategory()
                appCategory.setValuesForKeys(dict)
                appCategories.append(appCategory)
            }
            print(appCategories)
            DispatchQueue.main.async {
               // completionHandler(appCategories)
            }

        } catch let error as NSError {
            print(error.localizedDescription)
        }


    }.resume()

}

enter image description here然后是enter image description here,当然在它处理&#34; completionHandler&#34;它继续实现函数,就好像我发送它的简单闭包。enter image description here在return语句之前发现@escaping闭包调用,严格地说是在函数体中调用它的地方。

但我想也许我错了?也许苹果牢记另一种情况?请问我如何理解@escaping符号与Apples引用关于在返回后调用它们?实际上在它回来之前调用的例子中,为什么?

2 个答案:

答案 0 :(得分:4)

你说:

  

在我在函数体中调用它的地方严格地说@escaping闭包[被称为]。

是的,这恰恰发生了什么。无论您将其放在代码中的哪个位置,都可以调用它。如果您在从方法返回之前碰巧打电话给它,那就是它将会做什么。

正如其他人所指出的那样,它被声明为@escaping这一事实意味着可以稍后调用,而不是必然会被调用后面。

实际上,这种同步调用@escaping闭包的模式(即在方法返回之前)并不罕见。例如,如果您正在处理可以缓存响应的网络请求,则会看到此情况。在这种情况下,如果资源已被检索,您可以检查缓存并立即调用闭包,但如果之前没有缓存,则异步调用闭包,现在必须从Web异步检索资源。例如,你可能有类似的东西:

func fetchImage(for identifier: String, completion: @escaping (UIImage?) -> Void) {
    if let image = cache.retrieveImage(for: identifier) {
        completion(image)
        return
    }

    webService.fetchImageAsynchronously(for: identifier) { image in
        completion(image)
    }
}

注意,仅仅因为闭包被指定为@escaping,这并不意味着我的代码路径需要异步调用它,无论如何。我可以同步或异步地调用闭包,无论什么都有意义。

有人说过,如果你有一个方法,你知道你将总是同步调用闭包,你就不会使用@escaping指定。不仅在非转义场景中无偿使用@escaping会使代码不清楚,但@escaping指定会阻止编译器执行某些类型的优化。所以我们只使用@escaping所需的地方,即在那些我们知道它将会或可以异步调用的情况下。

答案 1 :(得分:2)

正如文件中所述:

  

当闭包作为参数传递给函数时,闭包被称为转义函数,但在函数返回后调用。当声明一个以闭包作为其参数之一的函数时,可以在参数的类型之前编写@escaping,以指示允许闭包转义。

在函数返回后,声明闭包@escaping不会使其执行。它只是意味着函数返回后可能可能不会执行,这完全取决于您的代码。

关于你的代码,你的闭包是在函数上下文中直接调用的,所以难怪在函数返回之前执行闭包。如果您希望在函数返回后执行它,您可能需要使用某些多线程机制,如GCDOperationQueue