今天的博客算是比較基礎的,還是那句話,基礎這東西在什么時候都是最重要的。說到函數(shù),只要是寫過程序就肯定知道函數(shù)是怎么回事,今天就來討論一下Swift中的函數(shù)的特性以及Swift中的閉包。今天的一些小實例中回類比一下Objective-C中的函數(shù)的寫法等等。Swift中的函數(shù)還是有許多好用的特性的,比如輸入?yún)?shù),使用元組返回多個值, 定義形參名,設定默認參數(shù)以及可變參數(shù)等等一些好用的特性。而在Swift中的閉包就是Objective-C中的Block, 除了語法不通外,兩者的用法是一樣的。廢話少說,開始今天的主題,先搞一搞Swift中的函數(shù),然后在搞一搞Swift中的閉包。
一.Swift中的函數(shù)
1. 函數(shù)的定義與使用
在介紹Swift中的函數(shù)之前,我想用Objective-C中的一個簡單的加法函數(shù)來作為引子,然后類比著實現(xiàn)一下Swift中相同功能的函數(shù)。關(guān)于函數(shù)定義就比較簡單了,就是一些語法的東西,下面的代碼片段是Objc中求兩個整數(shù)之和的函數(shù),并返回兩個數(shù)的和。
1
2
3-?(NSInteger)sumNumber1:(NSInteger)?number1
number2:(NSInteger)?number2?{returnnumber1?+?number2;
}
函數(shù)的功能比較簡單了,就是把兩個整數(shù)傳進來,然后返回兩個整數(shù)的和。接下來將用Swift語言實現(xiàn),也好通過這個實例來熟悉一下Swift語言中定義函數(shù)的語法。下方是Swift語言中求兩個整數(shù)之和的函數(shù)。語法比較簡單了,在Swift中定義函數(shù),我們會使用到關(guān)鍵字func來聲明函數(shù)。
1
2
3
4//函數(shù)定義
func?sum?(number1:Int,?number2:Int)?->?Int{
returnnumber1?+?number2;
}
用文字來描述Swift定義基本函數(shù)的語法就是:func函數(shù)名(形參列表) ->返回值類型{函數(shù)體},這樣你就可以定義一個函數(shù)了。當然,函數(shù)定義時還有好多其他的用法,下面會詳細介紹。上面函數(shù)的調(diào)用方法如下:
1
let?sumTwoNubmer?=?sum(2,?number2:?3);
2. 函數(shù)中的形參列表
關(guān)于函數(shù)中的形參列表還是有必要提上一嘴的,因為形參列表作為函數(shù)數(shù)據(jù)源之一,所以把參數(shù)列表好好的搞一搞還是很有必要的。參數(shù)列表也有很多好用的使用方式,接下來詳細的介紹一下函數(shù)的形參列表。
(1) 默認的形參是常量(let)
在函數(shù)的形參列表中,默認的形參是常量。也就是相當于用let關(guān)鍵字對形參進行修飾了。我們可以做個試驗,把上面加法函數(shù)做一個修改,在加法函數(shù)中對number1進行加1操作,你會得到一個錯誤,這個錯誤的大體意思就是“number1是不可被修改的,因為它是let類型的常量”。并且編譯器還給人出了Fix-it(修復)的方案,就是在number1前面使用var關(guān)鍵字進行修飾,使其成為變量,這樣才可以修改其值。
上面說這么多,一句話:形參默認是常量,如果你想讓其是變量,那么你可以使用var關(guān)鍵字進行修飾,這樣被關(guān)鍵字var修飾的變量在函數(shù)中就可以被修改。下方就是報的這個錯誤,和編譯器提供的解決方案。(在Objc中默認可以在函數(shù)中改變形參的值)

(2)給形參命名
為了代碼的可讀性和可維護性,我們在定義函數(shù)時,需要為每個參數(shù)名一個名字,這樣調(diào)用者見名知意,很容易就知道這個參數(shù)代表什么意思了。接下來還是在上述加法函數(shù)中進行修改,為每個參數(shù)名一個名字,并看一下調(diào)用方式。修改上面的函數(shù),給第一個形參命名成numberOne, 第二個形參為numberTwo, 下方是修改后的函數(shù)。 緊接著sum()函數(shù)的調(diào)用方式也會有所改變,在調(diào)用函數(shù)時編譯器會給出參數(shù)的名稱,這樣調(diào)用者一目了然。
1
2
3
4
5
6//函數(shù)定義
func?sum?(numberOne?number1:Int,?numberTwo?number2:Int)?->?Int{
returnnumber1?+?number2;
}
let?sumTwoNubmer?=?sum(numberOne:?10,?numberTwo:?20);
調(diào)用上述函數(shù)時,下方是編譯器給出的提示,一目了然呢。

關(guān)于Swift中參數(shù)名的內(nèi)容,要說明的是在Swift1.0的時候,你可以在參數(shù)前面添加上#號,然后參數(shù)名就與變量(或者常量)的名字相同,而Swift2.0后這個東西去掉了,因為默認就相當于Swift1.0中添加#號。
(3) 函數(shù)的傳參與傳引用
先暫且這么說著,在C語言的函數(shù)中可以給函數(shù)傳入?yún)?shù),或者傳入實參的內(nèi)存地址就是所謂的傳引用。如果傳入的是引用的話,在函數(shù)中對值進行修改的話,那么出了函數(shù),這個被修改的值是可以被保留的。在Swift中也是可以的,不過你需要使用inout關(guān)鍵字修飾形參,并且在使用該函數(shù)時,用&來修飾。這一點和C語言中類似,&就是取地址符。下方是inout使用的一個小實例。
1
2
3
4
5func?incrementStepTwo?(inout?myNumber:Int)?{
myNumber?+=?2
}
varmyTestNumber?=?6
incrementStepTow(&myTestNumber)//myTestNumber?=?8
myTestNumber變量經(jīng)過incrementStepTwo()函數(shù)后,其值就會增加2。當然前提是myTestNumber是變量,如果myTestNumber是常量的話,那么對不起,調(diào)用該函數(shù)就會報錯,下面是把var改成let后IDE給的錯誤提示。錯誤原因很顯然是你動了一個不該動的值,也就是常量不可再次被修改的。

(4) 不定參數(shù)函數(shù)
不定參數(shù)函數(shù)也就是形參的個數(shù)是不定的,但是形參的類型必須是相同的。不定形參在使用時怎么取呢?不定個數(shù)的形參實際上是一個數(shù)組,我們可以通過for循環(huán)的形式來遍歷出每個形參的值,然后使用就可以了。下方incrementMultableAdd()函數(shù)的形參的個數(shù)是不定的,其功能是求多個整數(shù)的和。在函數(shù)中我們只需遍歷每個參數(shù),然后把每個參數(shù)進行相加,最后返回所求的和即可。函數(shù)比較簡單,再此就不在啰嗦了。

(5) 默認形參值
在Swift語言中是支持給形參賦初始值的,這一點在其他一些編程語言中也是支持的。但是Objective-C這么看似古老的語言中就不支持給形參指定初始值,在Swift這門現(xiàn)代編程語言中是支持這一特性的。默認參數(shù)要從參數(shù)列表后開始為參數(shù)指定默認值,不然就會報錯。下方就是為函數(shù)的形參指定默認參數(shù)的示例。一個表白的方法sayLove(), 形參youName默認是“山伯”, 形參loverName默認是“英臺”。 緊接著是sayLove函數(shù)的三種不同的調(diào)用方式,在調(diào)用函數(shù)時你可以不傳參數(shù),可以傳一個參數(shù),當然傳兩個也是沒問題的。

因為函數(shù)的每個參數(shù)都是有名字的,在含有默認參數(shù)的函數(shù)調(diào)用時,可以給任意一個參數(shù)進行傳值,其他參數(shù)取默認值,這也是Swift的一大特色之一,具體請看如下簡單的代碼示例:

3.函數(shù)類型
每個函數(shù)都有自己的所屬類型,函數(shù)類型說白了就是如果兩個函數(shù)參數(shù)列表相同以及返回值類型相同,那么這兩個函數(shù)就有著相同的函數(shù)類型。在Swift中可以定義一個變量或者常量來存儲一個函數(shù)的類型。接下來將用過一個實例還介紹一下函數(shù)類型是個什么東西。
(1) 首先創(chuàng)建兩個函數(shù)類型相同的函數(shù),一個函數(shù)返回兩個整數(shù)的差值,另一個函數(shù)返回兩個整數(shù)的乘積。當然這兩個函數(shù)比較簡單,直接上代碼:
1
2
3
4
5
6
7
8//現(xiàn)定義兩個函數(shù)類型相同的函數(shù)
func?diff?(number1:Int,?number2:Int)?->?Int?{
returnnumber1?-?number2;
}
func?mul?(number1:Int,?number2:Int)?->?Int?{
returnnumber1?*?number2;
}
(2) 函數(shù)定義好后,接著要定義個一個枚舉來枚舉每種函數(shù)的類型,下面定義這個枚舉在選擇函數(shù)時會用到,枚舉定義如下:
1
2
3
4
5//定義兩種計算的枚舉類型
enum?CountType:Int?{
caseDiffCount?=?0
caseMulCount
}
(3) 接下來就是把(1)和(2)中定義的東西通過一個函數(shù)來組合起來。說白了,就是定義個函數(shù)來通過枚舉值返回這個枚舉值所對應的函數(shù)類型。有時候說多了容易犯迷糊,就直接上代碼得了。下方函數(shù)的功能就是根據(jù)傳進來的枚舉值來返回相應的函數(shù)類型。
1
2
3
4
5
6
7
8
9
10
11
12
13//選擇類型的函數(shù),并返回相應的函數(shù)類型
func?choiseCountType(countType:CountType)?->?((Int,?Int)?->?Int)?{
//函數(shù)類型變量
varmyFuncType:(Int,?Int)?->?Int
switchcountType?{
case.DiffCount:
myFuncType?=?diff
case.MulCount:
myFuncType?=?mul
}
returnmyFuncType;
}
(4) 接下來就是使用(3)中定義的函數(shù)了,首先我們需要定義一個相應函數(shù)類型((Int,Int) ->Int)的變量來接收choiseCountType()函數(shù)中返回的函數(shù)類型,然后調(diào)用該函數(shù)類型變量,在Playground中執(zhí)行的結(jié)果如下:

4.函數(shù)嵌套
我們可以把 3 中的代碼使用函數(shù)嵌套進行重寫,在Swift中是支持函數(shù)嵌套的。 所以可以吧3.1和3.2中的函數(shù)放到3.3函數(shù)中的,所以我們可以對上述代碼使用函數(shù)嵌套進行重寫。使用函數(shù)嵌套重寫后的代碼如下所示,當然,choiseCountType()函數(shù)的調(diào)用方式?jīng)]用發(fā)生改變,重寫后的調(diào)用方式和3.4中的調(diào)用方式是一樣一樣的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//選擇類型的函數(shù),并返回相應的函數(shù)類型
func?choiseCountType(countType:CountType)?->?((Int,?Int)?->?Int)?{
//現(xiàn)定義兩個函數(shù)類型相同的函數(shù)
func?diff?(number1:Int,?number2:Int)?->?Int?{
returnnumber1?-?number2;
}
func?mul?(number1:Int,?number2:Int)?->?Int?{
returnnumber1?*?number2;
}
//函數(shù)類型變量
varmyFuncType:(Int,?Int)?->?Int
switchcountType?{
case.DiffCount:
myFuncType?=?diff
case.MulCount:
myFuncType?=?mul
}
returnmyFuncType;
}
二. 閉包
說道Swift中的閉包呢,不得不提的就是Objective-C中的Block, 其實兩者是一個東西,使用方式以及使用場景都是相同的。我們完全可以類比著Objective-C中的Block來介紹一下Swift中的Closure(閉包)。其實就是匿名函數(shù)。接下來的這段內(nèi)容,先介紹一下Swift中Closure的基本語法,然后在類比著ObjC中的Block窺探一下Closure的使用場景。
1.Closure變量的聲明
Closure就是匿名函數(shù),我們可以定義一個閉包變量,而這個閉包變量的類型就是我們上面介紹的“函數(shù)類型”。定義一個閉包變量其實就是定義一個特定函數(shù)類型的變量,方式如下。因為Closure變量沒有賦初始值,所以我們把其聲明為可選類型的變量。在使用時,用!強制打開即可。
1
varmyCloure0:((Int,?Int)?->?Int)?
除了上面的方式外,我們還用另一種常用的聲明閉包變量的方式。那就是使用關(guān)鍵字typealias定義一個特定函數(shù)類型,我們就可以拿著這個類型去聲明一個Closure變量了,如下所示
1
2
3//定義閉包類型?(就是一個函數(shù)類型)
typealias?MyClosureType?=?(Int,?Int)?->?Int
varmyCloure:MyClosureType?
2. 給Closure變量賦值
給Closure變量賦值,其實就是把一個函數(shù)體賦值給一個函數(shù)類型的變量,和函數(shù)的定義區(qū)別不大。但是給閉包變量賦值的函數(shù)體中含有參數(shù)列表,并且參數(shù)列表和真正的函數(shù)體之間使用關(guān)鍵字in來分割。 閉包可選變量的調(diào)用方式與普通函數(shù)沒什么兩樣,唯一不同的是這個函數(shù)需要用!來強制打開才可以使用。賦值和調(diào)用方式如下。

3. 閉包回調(diào)的應用實例
暫且先稱作閉包回調(diào)吧,其實就是Objc中的Block回調(diào)。在Swift中的閉包回調(diào)和Objc中的Block回調(diào)用法一致,下方將會通過一個實例來介紹一下閉包的應用之一。下方會創(chuàng)建兩個視圖控制器,我們暫且稱為FirstViewController和SecondViewController。在FirstViewController上有一個Label和一個Button, 這個Button用來跳轉(zhuǎn)到SecondViewController, 而這個Label用來顯示從SecondViewController中回調(diào)過來的值。 而SecondViewController也有一個TextField和一個Button, 點擊Button就會把輸入框中的值通過閉包回調(diào)回傳到FirstViewController然后在FirstViewController上的Label顯示。
(1) 構(gòu)建這個實例的第一步要做的就是使用Stroyboard把我們所需的控件布局好,并且管理相應的類。當然我們這個Demo的重點不在于如何去布局控件,如何去關(guān)聯(lián)控件,以及如何去使用控件,所以上述的這些就不做贅述了。這個實例的重點在于如何使用Closure實現(xiàn)值的回調(diào)。下方是我們的控件布局和目錄結(jié)構(gòu)的截圖,從Storyboard上的控件來看,功能也就一目了然了。點擊“FirstViewController” 上的“Go SecondViewController”按鈕,就會跳轉(zhuǎn)到 “SecondViewController” 。 在SecondViewController視圖上的輸入框輸入數(shù)值,點擊Back按鈕返回到FirstViewController, 同時把輸入框中的文本通過閉包回調(diào)的形式回傳過來在FristViewController的label上顯示。大致就這個簡單的功能。

(2)FirstViewController.swift中的內(nèi)容
FirstViewController.swift中的內(nèi)容比較簡單,就關(guān)聯(lián)一個Label控件和一個按鈕點擊的事件,點擊按鈕就會跳轉(zhuǎn)到SecondViewController,具體代碼如下,在此就不啰嗦了,請看代碼中的注釋。下方代碼重要的一點是在跳轉(zhuǎn)到SecondViewController時要實現(xiàn)其提供的閉包回調(diào),以便接受回傳過來的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36//
//??FirstViewController.swift
//??SwiftDemo
//
//??Created?by?Mr.LuDashi?on?15/11/18.
//??Copyright???2015年?ZeluLi.?All?rights?reserved.
//
import?UIKit
class?FirstViewController:?UIViewController?{
@IBOutletvarshowTextLabel:?UILabel!//展示回調(diào)過來的文字信息
override?func?viewDidLoad()?{
super.viewDidLoad()
}
override?func?didReceiveMemoryWarning()?{
super.didReceiveMemoryWarning()
}
//點擊按鈕跳轉(zhuǎn)到SecondViewController
@IBAction?func?tapGoSecondViewControllerButton(sender:?UIButton)?{
//從Storyboard上加載SecondViewController
let?secondVC?=?UIStoryboard(name:"Main",?bundle:?NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("SecondViewController")as!?SecondViewController
//實現(xiàn)回調(diào),接收回調(diào)過來的值
secondVC.setBackMyClosure?{?(inputText:String)?->?Voidin
self.showTextLabel.text?=?inputText
}
//push到SecondViewController
self.navigationController?.pushViewController(secondVC,?animated:true)
}
}
(3) SecondViewController.swift中的內(nèi)容
SecondViewController.swift中的內(nèi)容也不麻煩,就是除了關(guān)聯(lián)控件和事件外,還定義了一個閉包類型(函數(shù)類型),然后使用這個特定的函數(shù)類型聲明了一個此函數(shù)類型對應的變量。我們可以通過這個變量來接受上個頁面?zhèn)鬟^來的閉包體,從而把用戶輸入的值,通過這個閉包體回傳到上個頁面。具體代碼實現(xiàn)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//
//??SecondViewController.swift
//??SwiftDemo
//
//??Created?by?Mr.LuDashi?on?15/11/18.
//??Copyright???2015年?ZeluLi.?All?rights?reserved.
//
import?UIKit
typealias?InputClosureType?=?(String)?->?Void//定義閉包類型(特定的函數(shù)類型函數(shù)類型)
class?SecondViewController:?UIViewController?{
@IBOutletvarinputTextField:?UITextField!//輸入框,讓用戶輸入值,然后通過閉包回調(diào)到上一個頁面
varbackClosure:InputClosureType?//接收上個頁面穿過來的閉包塊
override?func?viewDidLoad()?{
super.viewDidLoad()
}
override?func?didReceiveMemoryWarning()?{
super.didReceiveMemoryWarning()
}
//閉包變量的Seter方法
func?setBackMyClosure(tempClosure:InputClosureType)?{
self.backClosure?=?tempClosure
}
@IBAction?func?tapBackButton(sender:?UIButton)?{
ifself.backClosure?!=?nil?{
let?tempString:String??=?self.inputTextField.text
iftempString?!=?nil?{
self.backClosure!(tempString!)
}
}
self.navigationController!.popViewControllerAnimated(true)
}
}
(4) 經(jīng)過上面的步驟這個實例已經(jīng)完成,接下來就是看一下運行效果的時間了。本來想做成Git動態(tài)圖的,感覺實例功能簡單,而且UI上也比較簡單,就沒做,還是看截圖吧。運行效果的截圖如下:

4.數(shù)組中常用的閉包函數(shù)
在Swift的數(shù)組中自帶了一些比較好用的閉包函數(shù),例如Map, Filter, Reduce。接下來就好好的看一下這些閉包,用起來還是比較爽的。
(1) Map(映射)
說到Map的用法和功能,不能不說的是如果你使用過ReactiveCocoa框架,那么對里邊的Sequence中的Map的使用方式并不陌生。其實兩者的使用方法和功能是極為相似的。如果你沒使用過RAC中的Map,那也無關(guān)緊要,接下來我們先上段代碼開看一下數(shù)組中的Map閉包函數(shù)。

通過上面的代碼段以及運行結(jié)果,我們不難看出,map閉包函數(shù)的功能就是對數(shù)組中的每一項進行遍歷,然后通過映射規(guī)則對數(shù)組中的每一項進行處理,最終的返回結(jié)果是處理后的數(shù)組(以一個新的數(shù)組形式出現(xiàn))。當然,原來數(shù)組中的元素值是保持不變的,這就是map閉包函數(shù)的用法與功能。
(2) Filter (過濾器)
Filter的用法還是比較好理解的,F(xiàn)ilter就是一個漏勺,就是用來過濾符合條件的數(shù)據(jù)的。在ReactiveCocoa中的Sequence也是有Filter的,用法還是來過濾Sequence中的數(shù)據(jù)的。而在數(shù)組中的Filter用來過濾數(shù)組中的數(shù)據(jù),并且返回新的數(shù)組,新的數(shù)組中存放的就是符合條件的數(shù)據(jù)。Filter的用法如下實例,下方的實例就是一個身高的過濾,過濾掉身高小于173的人,返回大于等于173的身高數(shù)據(jù)。

(3)Reduce
在ReactiveCocoa中也是有Reduce這個概念的,ReactiveCocoa中使用Reduce來合并消減信號量。在swift的數(shù)組中使用Reduce閉包函數(shù)來合并items, 并且合并后的Value。下方的實例是一個Salary的數(shù)組,其中存放的是每個月的薪水。我們要使用Reduce閉包函數(shù)來計算總的薪水。下方是DEMO的截圖:
