Swift @escaping和Completion Handler

时间:2017-09-15 18:29:02

标签: swift escaping closures

我试图更准确地理解Swift的'关闭'。

@escapingCompletion Handler太难理解了

我搜索了许多Swift帖子和官方文档,但我觉得还不够。

这是官方文件的代码示例

var completionHandlers: [()->Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping ()->Void){
    completionHandlers.append(completionHandler)
}

func someFunctionWithNoneescapingClosure(closure: ()->Void){
    closure()
}

class SomeClass{
    var x:Int = 10
    func doSomething(){
        someFunctionWithEscapingClosure {
            self.x = 100
            //not excute yet
        }
        someFunctionWithNoneescapingClosure {
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)

completionHandlers.first?() 
print(instance.x)

我听说使用@escaping

有两种方式和原因

首先是存储闭包,第二个是用于Async操作目的。

以下是我的问题

首先,如果doSomething执行,那么someFunctionWithEscapingClosure将使用closure参数执行,并且该闭包将保存在全局变量数组中。

我认为封闭是{self.x = 100}

{self.x = 100}中self保存在全局变量completionHandlers中的instance如何能够连接到SomeClass someFunctionWithEscapingClosure的对象?

其次,我这样理解completionHandler

将局部变量闭包we using存储到全局变量'completionHandlers @escaping @escaping`关键字!

没有someFunctionWithEscapingClosure关键字completionHandler返回,本地变量@escaping将从内存中删除

$(document).ready(function () { $('.favorited').click(function () { SendFavorite(); }); }); function SendFavorite() { $.ajax({ type: 'POST', url: '/FavoriteMovie.php', dataType: 'json', encode: true }) // using the done promise callback .done(function (data) { console.log(data); }) .fail(function() { console.log('Error!'); // This one isn't reached. Because its 'succeeded'. }); } 将该闭包保留在内存中

这是对的吗?

最后,我只是想知道这种语法是否存在。

也许这是一个非常基本的问题。

如果我们想在某些特定功能之后执行某些功能。为什么我们不在特定函数调用后调用某个函数?

使用上述模式和使用转义回调函数有什么区别?

5 个答案:

答案 0 :(得分:88)

首先,我想说"非常好的问题:)"

完成处理程序:

假设用户在使用时正在更新应用。您肯定希望在完成后通知用户。你可能想要弹出一个框,上面写着:“恭喜,现在,你可以完全享受!”

那么,如何在下载完成后才运行代码块?此外,如何在视图控制器移动到下一个对象后才能为某些对象设置动画?好吧,我们将找出如何像老板一样设计一个。 根据我的扩展词汇表,完成处理程序代表

  

完成任务后做的事情

有关详细信息,请访问this blog post

此链接让我完全清楚完成处理程序(从开发人员的角度来看,它确切地定义了我们需要理解的内容)。

@escaping closures:

当你在函数的参数中传递闭包时,在函数体执行后使用它并返回编译器。当函数结束时,传递闭包的范围存在并且在内存中存在,直到闭包被执行。

有几种方法可以在包含函数中转义闭包:

  • 存储:当你需要将闭包存储在全局变量中时,属性或调用函数之前的内存中存在的任何其他存储都会被执行并返回编译器。

  • 异步执行:当您在发送队列上异步执行闭包时,队列会为您保留内存中的闭包,以后可以使用。在这种情况下,您不知道闭包何时会被执行。

当您尝试在这些场景中使用闭包时,Swift编译器将显示错误:

error screenshot

有关此主题的更多信息,请查看this post on Medium

我希望你能从这个链接中得到很好的理解。

如果您仍然有任何问题(但请务必先逐行阅读此链接;请仔细解释)然后随时分享您的评论。

谢谢,如果此答案需要更新,请继续发帖

答案 1 :(得分:17)

这是我用来提醒自己@转义如何工作的一小类示例。

class EscapingExamples: NSObject {

    var closure: (() -> Void)?

    func storageExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because `closure` is outside the scope of this
        //function - it's a class-instance level variable - and so it could be called by any other method at
        //any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
        //function.
        closure = completion
        //Run some function that may call `closure` at some point, but not necessary for the error to show up.
        //runOperation()
    }

    func asyncExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because the completion closure may be called at any time
        //due to the async nature of the call which precedes/encloses it.  We need to tell `completion` that it should
        //stay in memory, i.e.`escape` the scope of this function.
        DispatchQueue.global().async {
            completion()
        }
    }

    func asyncExample2(with completion: (() -> Void)) {
        //The same as the above method - the compiler sees the `@escaping` nature of the
        //closure required by `runAsyncTask()` and tells us we need to allow our own completion
        //closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until
        //it is executed, so our completion closure must explicitly do the same.
        runAsyncTask {
            completion()
        }
    }





    func runAsyncTask(completion: @escaping (() -> Void)) {
        DispatchQueue.global().async {
            completion()
        }
    }

}

答案 2 :(得分:1)

功能

您可以使用func关键字定义一个函数。函数可以包含多个参数,不返回任何一个或多个参数

关闭

闭包是可以独立传递的功能块,可以在代码中传递和使用。 Swift中的闭包类似于C和Objective-C中的块以及其他编程语言中的lambda。

函数和闭包,是迅速的一流类型:

  • 将函数/闭包分配给局部变量
  • 将函数/闭包作为参数传递
  • 返回功能/关闭

转义结束与非转义结束

  • non-escaping closure @noescape是一个闭包,它在传递给它的函数中即在返回之前被调用。

  • escaping closure @escaping是一个闭包,在将其传递给返回的函数之后调用。换句话说,它比传递给它的功能更持久。常见的用例是:

    • 异步调用;网络。
    • 函数存储为变量;思考动作和提供的回调。
    • 在调度队列上调度任务。

* {@noescape是swift2中的一个属性。 swift3不推荐使用此功能。 @noescape属性默认在Swift3中应用。由于默认情况下,闭包在Swift3中是不转义的,因此转义的闭包需要进行标记。而@escaping属性使我们可以做到这一点。

完成处理程序

escaping closure就是一个很好的例子。许多启动异步操作的函数都将闭包参数用作完成处理程序。该函数在开始操作后返回,但是直到操作完成后才调用闭包-该闭包需要转义,以后再调用。

在此处详细了解-Medium postMedium postdocs

答案 3 :(得分:1)

/*the long story short is that @escaping means that don't terminate the function life time until the @escaping closure has finished execution in the opposite of nonEscaping closure the function can be terminated before the closure finishes execution Ex:
*/

 func fillData(completion: @escaping: () -> Void){ 
     /// toDo 
    completion()
  }

//___________________________

//The call for this function can be in either way's @escaping or nonEscaping :

    
fillData{
 /// toDo
}
    

/* again the deference between the two is that the function can be terminated before finish of execution nonEscaping closure in the other hand the @escaping closure guarantees that the function execution will not be terminated before the end of @escaping closure execution. Hope that helps ***#(NOTE THAT THE CLOSURE CAN BE OF ANY SWIFT DATA TYPE EVEN IT CAN BE TYPEALIAS)*/
 

答案 4 :(得分:-1)

import UIKit
import Alamofire

//型号

class ShortlistCountResponse : Decodable {
    var response : String?
    var data : ShortlistcountData?

}
class ShortlistcountData : Decodable {

    var totalpropFavcount : Int?
    var totalprojFavcount : Int?

}

//通用类定义...

static func fetchGenericData<T: Decodable>(urlString: String,params : [String:Any], completion: @escaping (T) -> ()) {
        let url = urlString
        let headers = ["Content-Type": "application/x-www-form-urlencoded", "Accept":"application/json"]
        Alamofire.request(url, method: .post, parameters:params, encoding: URLEncoding.default, headers: headers).responseJSON { response in
            print(response.request?.urlRequest ?? "")
            print(params)
            print(response.data ?? "")
            print(response.value ?? "")
            switch(response.result) {
            case .success(_):
                if let data = response.data{
                    do {
                        let gotData = try JSONDecoder().decode(T.self, from: data)
                        completion(gotData)

                    }
                    catch let jsonErr {
                        print("Error serializing json:", jsonErr)
                        ActivityIndicator.dismissActivityView()

                    }
                    DispatchQueue.main.async {
                        ActivityIndicator.dismissActivityView()
                    }
                }
                break
            case .failure(_):
                print(response.result.error ?? "")
                ActivityIndicator.dismissActivityView()


                break

            }
        }
}

//有趣的通话

override func viewDidLoad() {
    super.viewDidLoad()

            let userID = ""
            let languageID = ""
            let params = ["userID":userID,"languageID":languageID]
            var appDelegate: AppDelegate?
            Service.fetchGenericData(urlString: "your url...", params: params) { (shortlistCountResponse : ShortlistCountResponse) in
             print(shortListCountResponse.data.totalprojFavcount ?? 0)

            }
}