简体   繁体   English

如何在Swift中传递参数未知且返回值作为参数的函数

[英]How to pass a function with unknown parameters and returned value as a parameter in Swift

I would like to pass a function as a parameter because I am dealing with web services, and I have noticed that the code is repetitive. 我想将函数作为参数传递,因为我正在处理Web服务,并且我注意到该代码是重复的。

Snippet 1 片段1

Service.getAllVouchersUsingCallback() { (response, data, error) -> Void in
    guard let statusCode = response?.statusCode else {
        Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
        return
    }

    switch statusCode {
        case 200:
            self.loadVouchersWithData(data!)
        case 503:
            Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
        default:
            Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
    }
}

Snippet 2 片段2

Service.getAllCategoriesUsingCallback { (response, data, error) -> Void in

    guard let statusCode = response?.statusCode else {
        Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
        return
    }

    switch statusCode {
        case 200:
            self.loadAndGetCategories(data!, withInialText: "Category ")
        case 503:
            Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
        default:
            Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
    }
}

The part that is repetitive is what happens when the status code is nil , and the action I have to perform when the response is 200 . 重复的部分是状态码为nil时发生的情况,以及响应为200时我必须执行的操作。 I guess the function signature should be like this: 我猜功能签名应该是这样的:

func dealWithWebServiceResponse(response: NSURLResponse?, withData data: NSData?, whichActionIs action: whateverFunctionType)

So, I would like to know how I can pass whatever function, ie, whatever number of parameters or whatever number of return values because in this case I am passing just data, but probably in the future I will need another kind of function. 因此,我想知道如何传递任何函数,即传递任意数量的参数或返回数量的返回值,因为在这种情况下,我仅传递数据,但是将来可能需要另一种函数。

Thanks in advance. 提前致谢。

This is a great problem to explore functions that return functions. 探索返回函数的函数是一个很大的问题。 So we have this block of code: 因此,我们有以下代码块:

guard let statusCode = response?.statusCode else {
    Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
    return
}

switch statusCode {
case 200:
    // <<================ Right here, we want to do "something different"
case 503:
    Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
    Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}

So how do we do "something different?" 那么,我们该如何做一些“不同的事情?” We pass a function. 我们传递一个函数。 That function needs to take "data" because that's the only thing we have. 该函数需要获取“数据”,因为这是我们唯一的东西。 You might be thinking that the function takes "other things" (like "Category "), but it really doesn't. 您可能会认为该函数需要“其他内容”(例如“ Category”(类别)),但实际上没有。 This code doesn't know anything about "Category ". 这段代码对“ Category”一无所知。 Something else, earlier in the program, has to have dealt with that part. 在程序的较早部分,必须处理该部分。 The only thing that varies here is the data. 唯一变化的是数据。 So let's pretend we have that function for a second: 因此,让我们假装一下该功能:

let success: (NSData) -> Void = ...   
...
case 200:
   success(data!)
...

We just want to figure out what success is in this case. 我们只想弄清楚在这种情况下success是什么。 Well, in your first example, it's: 好吧,在您的第一个示例中,它是:

{ self.loadVouchersWithData($0) }

and in your second example it's: 在第二个示例中,它是:

{ self.loadAndGetCategories($0, withInialText: "Category ") }

Those are both functions that take an NSData and return nothing, just like we want. 这两个函数都需要一个NSData并且不返回任何内容,就像我们想要的那样。

So we want a way to take that first block of code and plug-in this thing that changes. 因此,我们希望有一种方法来采用第一段代码并将此变化的插件插入。 We want a function that takes an "on success" function and returns a "handle all the stuff" function. 我们需要一个带有“成功”功能并返回“处理所有任务”功能的功能。 Let's write that out the long way: 让我们把它写得很长:

func successHandler(success: (NSData) -> Void) -> (NSHTTPURLResponse?, NSData?, NSError?) -> Void {
    return { (response: NSHTTPURLResponse?, data: NSData?, error: NSError?) in
        guard let statusCode = response?.statusCode else {
            Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
            return
        }

        switch statusCode {
        case 200:
            success(data!) // <==== Here's the part that changes!
        case 503:
            Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
        default:
            Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
        }
    }
}

Whoa, that first line is a doozy. 哇,那第一行很傻。 Let's look at it: 让我们看一下:

func successHandler(success: (NSData) -> Void) -> (NSHTTPURLResponse?, NSData?, NSError?) -> Void {

This is a function, that takes a function that takes an NSData and returns nothing, and that whole function returns a function that takes a response,data,error tuple, and returns nothing. 这是一个函数,该函数接受一个NSData且不返回任何内容的函数,整个函数返回一个接受响应,数据,错误元组且不返回任何值的函数。 Meditate on that for a moment. 沉思片刻。 You really want that to sink in because it's really, really powerful. 您真的希望它沉入其中,因为它确实非常强大。

OK, hopefully that's starting to sink in a little bit, so I'm going to move on. 好的,希望这会开始有所下降,所以我将继续。 The syntax is pretty enormous, so Swift gives us a nice trick to simplify it, called currying: 语法非常庞大,因此Swift提供了一个很好的技巧来简化它,称为currying:

func successHandler(success: (NSData) -> Void)(response: NSHTTPURLResponse?, data: NSData?, error: NSError?) {
    guard let statusCode = response?.statusCode else {
        Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
        return
    }

    switch statusCode {
    case 200:
        success(data!) // <==== Here's the part that changes!
    case 503:
        Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
    default:
        Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
    }
}

The declaration is now: 现在的声明是:

func successHandler(success: (NSData) -> Void)(response: NSHTTPURLResponse?, data: NSData?, error: NSError?) {

(I know that probably doesn't seem much simpler, but it really is, and it definitely makes the rest of the function simpler.) (我知道这似乎似乎并不简单,但实际上确实如此,并且它肯定会使其余函数更简单。)

That is (almost) exactly identical to the previous declaration. (几乎)与先前的声明完全相同。 Mediate on that line for a moment. 在那条线上暂时进行调解。 Note the f(x: T)(y: U) double-parentheses syntax. 请注意f(x: T)(y: U)双括号语法。 Note where I could drop -> Void at the end. 请注意,我可以在最后放置-> Void

Currying is like passing some parameters now, and being able to pass the rest later. 咖喱就像现在传递一些参数,然后能够传递其余参数。

OK, so how do we use that? 好,那我们怎么用呢?

Service.getAllVouchersUsingCallback(successHandler{ self.loadVouchersWithData($0) })
Service.getAllCategoriesUsingCallback(successHandler{ self.loadAndGetCategories($0, withInialText: "Category ") })

We call our thing that wants a (response,data,error) and pass it the result of calling successHandler with a function that takes a data. 我们将我们想要的东西称为(响应,数据,错误),并将其传递给带有数据的函数调用successHandler的结果。

And that should remove all the duplication you were talking about. 这应该删除您正在谈论的所有重复项。 This is a particularly complicated version of currying because there are a lot of levels of functions. 这是currying的一个特别复杂的版本,因为有很多功能级别。 But it also shows how powerful the technique is. 但这也显示了这项技术的强大功能。

You may want to put this aside for a moment and go back to a simpler introduction such as Introduction to Function Currying in Swift . 您可能需要暂时搁置一下,然后再回到简单的介绍中,例如Swift中的函数库简介 Then, when that makes sense, come back to this. 然后,当这有意义时,请回到此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM