关于Swift的闭包,closure以及其在可选

对于新手(如笔者)来说,闭包是Swift语言特性中很难理解的一部分。其原因可能有一部分在于一些人把复杂的函数写得很简单,新手会看不懂。闭包可以理解为把函数当做一个变量来看待。既然是变量,那么就自然可以作为参数传递给其它的函数。也就是说,实际上是把一个函数传递给了另一个函数。本文将详解闭包以及其在Optional型中的应用。

我们知道Swift采用了一种非常安全的称之为Optional的类型。这个Optional类型只可能有两种状态,一种是“什么都没有”(nil),另一种是“有什么东西”,只有这两种状态。对于第二种“有什么东西”的状态而言,有的东西即是写在问号之前的那个类型。需要提醒的是,例如 Int? 类型并不是“可以为空的整型”,而是“可以含有整型的Optional型”。

现在有一个课题,要求如下:

想返回整数a的平方,在a为空(nil)的情况下返回空(nil)。

如果用Optional binding来写的话可以写成下面这样

let result: Int?
if let a0 = a {
    result = a0 * a0
}

当然,为了让代码含义更清楚,也可以在 if { } 后面加上

else {
    result = nil
}

这当然没有问题,但是很遗憾的是仅仅为了得到一个整数的平方便要写这么多的代码实在是不值当。虽说optional很安全但是代价也不小。仔细想想也可以像下面这样简化代码。

let result: Int? = a == nil ? nil : a! * a!

虽然理论上可行,代码也很简单,但是一看就会发现,这显然不是Swift的风格。而且更重要的是,感叹号!是不安全的,应该避免使用。

在考虑代码如何写之前,先来回顾一下何为Optional类型:Optional型是可以有某值的可选类型。实际上,我们是有不把值从Optional中取出来而直接操作的办法的。

先来给出上面问题的答案:

let result: Int? = a.map { $0 * $0 }

如此便能够实现上述的功能。

为了便于理解,我们来逐步分析。

首先,Optional型的map函数提供了一个功能,可以不拆开Optional型而直接对其内部的变量进行操作。但是我们需要为map函数传递一个参数,该参数实际上又是一个函数,可以实现将整数平方的功能。所以,首先我们来写这样一个函数。

func square(someInteger: Int) -> Int {
      return someInteger * someInteger
}

接下来,我们只要把square函数作为自变量传递给map函数即可。

let result: Int? = a.map(square)

这样一来便可以实现上面的目的,接下来我们继续简化这段代码。我们可以把square的定义部分直接传递给map函数,这样也是可以实现目的的。具体如下:

let result: Int? = a.map({
    (someInteger: Int) -> Int in
    return someInteger * someInteger
})

区别在于,大括号原先的位置由 in 取代,而大括号则移动到了函数最前方。我们还知道,函数的返回值可以默认为最后一行代码的返回值,所以也可以删掉 return

,这样代码就变成了

let result: Int? = a.map({
    (someInteger: Int) -> Int in
    someInteger * someInteger
})

Swift是强语言类型,这意味着它可以根据上下文轻易推断出变量的类型。所以当变量类型显而易见的时候,也可以省略不写。代码就可以简化为

let result: Int? = a.map({
    someInteger in
    someInteger * someInteger
})

最后,函数的自变量名也可以简化(额。。不是省略),自变量在没有特殊命名时,默认按顺序用 $0 , $1 等来表示。所以someInteger这个变量就可以用$0代替,于是上面的代码还可以简化为:

let result: Int? = a.map({ $0 * $0 })

最后,作为自变量传递给map函数的函数可以写在小括号外面,即

let result: Int? = a.map() { $0 * $0 }

小括号也可以省略不写,于是便得到了上面的答案。

以上为闭包以及其简单的应用。根据用法,闭包可以大大简化代码,亦可以增强代码可读性。对于新手而言,确实是一个不好理解的知识点,需要反复琢磨。