前言
Swift中函數(shù)的使用是非常靈活的,比如,函數(shù)有自己的類型,可以當(dāng)作參數(shù)、返回值,還有Swift中還引入了函數(shù)式編程。
一、函數(shù)類型
1、什么是函數(shù)類型
每個(gè)函數(shù)都有特定的函數(shù)類型,函數(shù)類型是有函數(shù)的參數(shù)和返回值共同組成的。
(1) func addTwoInts(a:Int,_b:Int)->Int{ return a+b } (2) fun printHelloWorld(){ } (3) fun mathFunction()->Int{ }
上面三個(gè)函數(shù)的類型分別是(Int,Int)->Int,()->void,()->Int
在Swift中我們可以把函數(shù)類型像其他的類型一樣使用,比如我們可以把函數(shù)類型聲明成var,let,可以把函數(shù)類型當(dāng)作參數(shù),當(dāng)作返回值等。
func addTwolints(a:Int,b:Int)->Int{ return a+b } func subtractionFuction(a:Int,b:Int)->Int{ return a - b } let addFuction:(Int,Int)->Int = addTwolints var subFuction:(Int,Int)->Int = subtractionFuction subsection = addFuction//類型一樣的才能這么賦值 let result:Int = subsection(5,5)
2、函數(shù)類型怎么使用范圍
函數(shù)類型可以做常量、變量參數(shù)、返回值,下面有幾個(gè)小例子可以很好的說(shuō)明
(1)、函數(shù)類型做常量和變量
func addTwolints(a:Int,b:Int)->Int{ return a+b } func subtractionFuction(a:Int,b:Int)->Int{ return a - b } let addFuction:(Int,Int)->Int = addTwolints//定義一個(gè)(Int,Int)->Int類型的常量addFuction var subFuction:(Int,Int)->Int = subtractionFuction subsection = addFuction//類型一樣的才能這么賦值 let result:Int = subsection(5,5)
(2)、函數(shù)類型做參數(shù)
函數(shù)有三個(gè)參數(shù)有一個(gè)(Int,Int)->Int類型的mathFunction,和兩個(gè)Int類型
\* printMathResult()函數(shù)有三個(gè)參數(shù) *\ func printMathResult(mathFunction:(Int,Int)->Int,a:Int,b:Int){ print("Result:\(math)") } printMathResult(addTwolints,3,5)
(3)、函數(shù)類型做返回值
同樣貼出官方給出的例子
fun setpForward(a:Int)->Int{ return a + 1 } func setpBackward(b:Int)->Int{ return b - 1 } func chooseStepFuction(backwards:Bool)->(Int)->Int{ return backwards ? setpForward: setpBackward }
二、閉包
感覺(jué)Swift中的函數(shù)也很簡(jiǎn)單明了,好吧,這兒有兩個(gè)字符串看看他們是什么?怎么會(huì)有這么奇葩的寫(xiě)法
(a)、var myLowerString:String = { "myLowerString" }() (b)、 var myMaxString:String{ return "myMaxString" }
看上去是不是不好明白,其實(shí)上面兩個(gè)是一樣的他們都是使用Swift中的閉包給字符串賦值,只是寫(xiě)法進(jìn)化了,現(xiàn)在我們一步一步的讓它退化
(1)、上面a式可以退化成這樣:
var myLowerString:String = { return "myLowerString" }()
只是多了個(gè)return還是不好明白
再次退化可以寫(xiě)成這樣:
var myLowerString2:String = { () in return "myLowerString" }()
目前這個(gè)樣子是不是已經(jīng)很接近Closures的標(biāo)準(zhǔn)寫(xiě)法了,換一個(gè)式子加兩個(gè)參數(shù)就是標(biāo)準(zhǔn)寫(xiě)法:
var myLowerString3:String = { (s1,s2) in return s1 + s2 }("hello","world")
補(bǔ)上兩個(gè)參數(shù)(a)式終于露出了本來(lái)面目,上面的(b)式經(jīng)過(guò)這樣一步退化就會(huì)變成(a)式:
var myMaxString:String={ return "myMaxString" }()
好吧,如果我把Closures認(rèn)為也是函數(shù)的一種特殊形式,或者反之那么Swift中函數(shù)的靈活度我已經(jīng)無(wú)力吐槽了,其實(shí)這只是剛剛開(kāi)始下面函數(shù)式編程會(huì)使其更加靈活。
1、Closures的表達(dá)式
上面舉了個(gè)閉包的例子,現(xiàn)在照著官方文檔一步步學(xué)習(xí),閉包是自包含代碼塊,可以在代碼中傳遞和使用(如果認(rèn)為它是函數(shù)這個(gè)也很好理解),當(dāng)然在Objective-C中我們把這種寫(xiě)法稱為blocks(代碼塊)。官方文檔說(shuō)閉包有三種形式:
(a)、全局函數(shù):全局函數(shù)是一個(gè)有名字但不會(huì)捕獲任何值的閉包
(b)、嵌套函數(shù):是一個(gè)有名字并可以捕捉起封閉函數(shù)域內(nèi)值的閉包
(c)、閉包表達(dá)式:閉包表達(dá)式是一個(gè)利用輕量級(jí)語(yǔ)法縮寫(xiě)的可以捕獲其上下文變量或常量的匿名閉包
(1)、全局函數(shù)和嵌套函數(shù)
對(duì)于全局函數(shù)和嵌套函數(shù)官方文檔是這么說(shuō)的:
All of the functions you have encountered so far in this chapter have been examples of global functions, which are defined at a global scope. You can also define functions inside the bodies of other functions, known as nested functions.雖然沒(méi)有找出明確的說(shuō)明但是我覺(jué)得函數(shù)就是閉包?
這兒貼出兩個(gè)函數(shù)作為參考:
func sayHello(personName: String) -> String { let greeting = "Hello, " + personName + "!" return greeting }//這個(gè)全局函數(shù) func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backwards ? stepBackward : stepForward }// 這個(gè)嵌套函數(shù)理解起來(lái)很容易
(2)、捕獲值(Capturing Values)
在全局函數(shù)和嵌套函數(shù)中有出現(xiàn)了一個(gè)詞“捕獲值(Capturing Values)”,官方解釋是這樣的:閉包可以在其被定義的上下文中捕獲常量或變量。即使定義這些常量和變量的原作用域已經(jīng)不存在,閉包仍然可以在閉包函數(shù)體內(nèi)引用和修改這些值,大致意思也就是說(shuō)閉包可以在它被定義的上下文中捕捉和持有變量和常量。官方使用嵌套函數(shù)很好解釋了這個(gè)概念
func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }
makeIncrementor函數(shù)需要輸入Int類型的參數(shù)amount,返回一個(gè) () -> Int類型的函數(shù),里面定義個(gè)一個(gè)可變的runningTotal,嵌套了一個(gè)incrementor函數(shù),不需要參數(shù),返回Int類型。我們可以看到內(nèi)嵌函數(shù)incrementor拷貝并持有了一份amount,引用并持有了runningTotal。這樣閉包持有的值占用的內(nèi)存在什么時(shí)候釋放,就有閉包去管理了。(說(shuō)到這兒插入一句題外話,由于閉包的這個(gè)(捕獲值)特性,閉包中如果出現(xiàn)self,self就會(huì)被閉包持有,如果實(shí)例直接或者間接的引用閉包就會(huì)造成一個(gè)”完美的環(huán)“,self->閉包->self,這樣循環(huán)引用就悄無(wú)聲息的形成了)執(zhí)行下面的代碼:
let incrementByTen = makeIncrementor(forIncrement: 10) print(incrementByTen())//10 print(incrementByTen())//20 print(incrementByTen())//30
(3)、閉包表達(dá)式
現(xiàn)在終于輪到主角閉包表達(dá)式登場(chǎng)了,閉包表達(dá)式:閉包表達(dá)式是一種利用簡(jiǎn)潔語(yǔ)法構(gòu)建內(nèi)聯(lián)閉包的方式官方說(shuō)他們對(duì)Swift的閉包進(jìn)行了下列優(yōu)化
(a)、利用上下文推斷參數(shù)和返回值類型 (b)、隱式返回單表達(dá)式閉包,即單表達(dá)式閉包可以省略return關(guān)鍵字 (c)、參數(shù)名稱縮寫(xiě) (d)、尾隨(Trailing)閉包語(yǔ)法
上面這些就是閉包寫(xiě)法進(jìn)化的依據(jù)。再用官方的例子吧,畢竟我是寫(xiě)不出也想不出比官方給的例子更能表達(dá)問(wèn)題的例子了。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = names.sort(backwards)// reversed 為 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
sort函數(shù)可以根據(jù)傳入的排序函數(shù)對(duì)數(shù)組進(jìn)行排序,并返回重新排序的數(shù)組。
如果傳入閉包就應(yīng)該是這樣首先貼出閉包語(yǔ)法的一般形式:
{ (parameters) -> returnType in statements }
var reversed = names.sort({ (s1: String, s2: String) -> Bool in return s1 > s2 })
這么寫(xiě)跟上面寫(xiě)有什么卻別?其實(shí)沒(méi)有區(qū)別,只不過(guò)是把有名字的閉包(backwards)換成沒(méi)名字的閉包(閉包表達(dá)式)
上面說(shuō)了閉包表達(dá)式有上下文推斷功能(Inferring Type From Context),那么由于names是字符串類型的數(shù)組,所有s1,s2類型應(yīng)該可以推斷出來(lái)上面的閉包表達(dá)式就可以開(kāi)始進(jìn)化成這樣:
var reversed = names.sort( { s1, s2 in return s1 > s2 } )
單表達(dá)式隱式返回(Implicit Return From Single-Expression Clossures)又是怎么回事?
簡(jiǎn)單的說(shuō)就是如果in后面的表達(dá)式只有return這一行,那么就可以省略掉return就像下面這樣
reversed = names.sort( { s1, s2 in s1 > s2 } )
參數(shù)名縮寫(xiě)(Shorthand Argument Names)這個(gè)技能,所謂參數(shù)名縮寫(xiě)就是把閉包中的參數(shù)名省掉,而是用$0,$1,$2有序的替代參數(shù)。這樣說(shuō)的話,上面的式子可以接著簡(jiǎn)寫(xiě)成這樣:
reversed = names.sort( { $0 > $1 } )
如果你覺(jué)得這樣還不夠簡(jiǎn)練那我們還可以接著簡(jiǎn)寫(xiě)只保留一個(gè)運(yùn)算符號(hào),干脆參數(shù)都不要了:
reversed = names.sort(>)
所有技能都用上進(jìn)化成這種程度已經(jīng)算是終極進(jìn)化了吧。
4、尾隨閉包(Trailing Closures)
尾隨閉包需要一個(gè)條件:閉包作為最后一個(gè)參數(shù),同時(shí)尾隨閉包體現(xiàn)在函數(shù)的調(diào)用上.
下面的例子就是使用尾隨閉包的對(duì)比:
``
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函數(shù)體部分
print("someFunctionThatTakesAClosure")
}
//不使用函數(shù)閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure { () -> Void in
//閉包主體部分
}
//使用函數(shù)閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure(){
//閉包主體部分
}
如果使用上面sort函數(shù)進(jìn)行句例子,那么sort函數(shù)應(yīng)該是這樣的
reversed = names.sort(){>}
官方又說(shuō)了,如果這個(gè)函數(shù)的參數(shù)只有這么一個(gè)閉包的話那么有可以這樣簡(jiǎn)寫(xiě):
reversed = names.sort{>}
寫(xiě)到這兒我不得不吐槽了,搞這么靈活是故意折麼我們程序員嗎?是不是有點(diǎn)懷念Object-C了? 為了安慰我們官方有舉了個(gè)例子,告訴我們這么寫(xiě)的好處:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
print(strings)
上面的map函數(shù)是這樣的,map方法獲取一個(gè)閉包表達(dá)式作為唯一參數(shù),閉包函數(shù)會(huì)對(duì)數(shù)組中的元素每個(gè)調(diào)用一次,并且返回該元素所映射的值,拘役的映射方法和返回值類型,由閉包決定,說(shuō)白了,也就是,閉包方法處理數(shù)組中的每一個(gè)值,并返回處理后的結(jié)果,numbers經(jīng)過(guò)[%10] = [6,8,0],對(duì)應(yīng)的字符串是:Six,Eight,Zero,經(jīng)過(guò)number /= 10處理 = [1,5,51]對(duì)應(yīng)的字符串是One,F(xiàn)ive,OneZero,所以strings = ["OneSix", "FiveEight", "FiveOneZero"]