對於新手(如筆者)來說,閉包是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 }
小括號也可以省略不寫,於是便得到了上面的答案。
以上為閉包以及其簡單的應用。根據用法,閉包可以大大簡化代碼,亦可以增強代碼可讀性。對於新手而言,確實是一個不好理解的知識點,需要反復琢磨。