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

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

我想将函数作为参数传递,因为我正在处理 Web 服务,并且我注意到代码是重复的。

片段 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)
    }
}

片段 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)
    }
}

重复的部分是状态代码为nil时发生的情况,以及响应为200时我必须执行的操作。我想函数签名应该是这样的:

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

所以,我想知道如何传递任何函数,即任何数量的参数或任何数量的 return 值,因为在这种情况下我只传递数据,但可能在未来我需要另一种功能。

提前致谢。

探索return函数的函数是一个很好的问题。所以我们有这段代码:

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)
}

那么我们如何做 "something different?" 我们传递一个函数。该函数需要使用 "data" 因为这是我们唯一拥有的东西。您可能认为该函数采用 "other things"(类似于 "Category "),但实际上并非如此。 这个代码对"Category "一无所知。在程序的早期,必须有其他东西处理该部分。这里唯一不同的是数据。因此,让我们假装我们拥有该功能一秒钟:

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

我们只想弄清楚success在这种情况下是什么。那么,在您的第一个示例中,它是:

{ self.loadVouchersWithData([=12=]) }

在你的第二个例子中是:

{ self.loadAndGetCategories([=13=], withInialText: "Category ") }

这两个函数都不需要 NSData 和 return,就像我们想要的那样。

所以我们想要一种方法来获取第一个代码块并插入这个改变的东西。我们想要一个接受 "on success" 函数和 return 一个 "handle all the stuff" 函数的函数。让我们把它写下来:

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)
        }
    }
}

哇哦,第一行真是太棒了。一起来看看吧:

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

这是一个函数,它接受一个接受 NSData 的函数,return什么都没有,而整个函数 return 是一个接受响应、数据、错误元组和 return没什么。想一想。你真的很想让它深入人心,因为它真的非常强大。

好吧,希望这会开始一点点下沉,所以我要继续前进。语法非常庞大,所以 Swift 给了我们一个很好的技巧来简化它,称为柯里化:

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)
    }
}

现在的声明是:

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

(我知道这可能看起来并没有简单多少,但它确实如此,而且它确实使函数的其余部分更简单。)

这与之前的声明(几乎)完全相同。在那条线上调解片刻。请注意 f(x: T)(y: U) 双括号语法。请注意我可以在最后放置 -> Void 的地方。

柯里化就像现在传递一些参数,然后可以传递其余的参数。

好的,那么我们如何使用它呢?

Service.getAllVouchersUsingCallback(successHandler{ self.loadVouchersWithData([=18=]) })
Service.getAllCategoriesUsingCallback(successHandler{ self.loadAndGetCategories([=18=], withInialText: "Category ") })

我们调用我们想要一个 (response,data,error) 的东西,并将调用 successHandler 的结果传递给它,函数接受一个数据。

这应该会删除您所说的所有重复项。这是一个特别复杂的柯里化版本,因为函数有很多层次。但这也说明了这项技术的强大。

您可能想暂时搁置一下,回到更简单的介绍,例如 Introduction to Function Currying in Swift。然后,当这有意义时,回到这个。