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

时间:2015-11-04 16:54:59

标签: ios swift function parameters swift2

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

Snippet 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

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)

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

提前致谢。

1 个答案:

答案 0 :(得分:3)

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

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

那么我们该如何做和不同的事情?&#34;我们传递一个函数。该功能需要采取&#34;数据&#34;因为这是我们唯一拥有的东西。您可能认为该功能需要&#34;其他事情&#34; (比如&#34;类别&#34;),但它确实没有。 代码对&#34;类别&#34;一无所知。程序早期的其他东西必须处理那个部分。这里唯一不同的是数据。因此,让我们假装我们拥有该功能一秒钟:

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

我们只想弄清楚success在这种情况下是什么。好吧,在你的第一个例子中,它是:

{ self.loadVouchersWithData($0) }

在你的第二个例子中:

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

这两个函数都采用NSData并且不返回任何内容,就像我们想要的那样。

所以我们想要一种方法来获取第一块代码并插入这个变化的东西。我们想要一个成功的功能&#34;功能并返回一个&#34;处理所有的东西&#34;功能。让我们写出很长的路要走:

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并且不返回任何内容的函数,并且整个函数返回一个函数,该函数接收响应,数据,错误元组,并且不返回任何内容。冥想一下。你真的希望自己陷入其中,因为它真的非常强大。

好的,希望它开始有点下沉,所以我要继续前进。语法非常庞大,因此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)
    }
}

声明现在是:

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

(我知道这可能看起来并不简单,但确实如此,这肯定会使函数的其余部分变得更简单。)

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

Currying就像现在传递一些参数一样,以后能够传递其余参数。

好的,我们如何使用它?

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

我们称之为需要(响应,数据,错误)的东西,并将调用successHandler的结果传递给带有数据的函数。

这应该删除你所谈论的所有重复。这是一个特别复杂的currying版本,因为有很多级别的功能。但它也表明了这项技术的强大程度。

你可能想暂时搁置这一点,然后回到Introduction to Function Currying in Swift之类的简单介绍。然后,当这有意义时,回过头来看。