窺探 Swift 之 函數與閉包的應用實例

窺探 Swift 之 函數與閉包的應用實例

 

今天的博客算是比較基礎的,仍是那句話,基礎這東西在何時都是最重要的。說到函數,只要是寫過程序就確定知道函數是怎麼回事,今天就來討論一下Swift中的函數的特性以及Swift中的閉包。今天的一些小實例中回類比一下Objective-C中的函數的寫法等等。Swift中的函數仍是有許多好用的特性的,好比輸入參數,使用元組返回多個值, 定義形參名,設定默認參數以及可變參數等等一些好用的特性。而在Swift中的閉包就是Objective-C中的Block, 除了語法不通外,二者的用法是同樣的。廢話少說,開始今天的主題,先搞一搞Swift中的函數,而後在搞一搞Swift中的閉包。編程

一.Swift中的函數swift

1. 函數的定義與使用數組

在介紹Swift中的函數以前,我想用Objective-C中的一個簡單的加法函數來做爲引子,而後類比着實現一下Swift中相同功能的函數。關於函數定義就比較簡單了,就是一些語法的東西,下面的代碼片斷是Objc中求兩個整數之和的函數,並返回兩個數的和。閉包

1框架

2編程語言

3ide

- (NSInteger)sumNumber1:(NSInteger) number1函數

                number2:(NSInteger) number2 {    return number1 + number2;佈局

}spa

函數的功能比較簡單了,就是把兩個整數傳進來,而後返回兩個整數的和。接下來將用Swift語言實現,也好經過這個實例來熟悉一下Swift語言中定義函數的語法。下方是Swift語言中求兩個整數之和的函數。語法比較簡單了,在Swift中定義函數,咱們會使用到關鍵字func來聲明函數。

1

2

3

4

//函數定義

 func sum (number1:Int, number2:Int) -> Int{

     return number1 + number2;

 }

用文字來描述Swift定義基本函數的語法就是: func 函數名 (形參列表) -> 返回值類型 { 函數體},這樣你就能夠定義一個函數了。固然,函數定義時還有好多其餘的用法,下面會詳細介紹。上面函數的調用方法以下:

1

let sumTwoNubmer = sum(2, number2: 3);

 

2. 函數中的形參列表

關於函數中的形參列表仍是有必要提上一嘴的,由於形參列表做爲函數數據源之一,因此把參數列表好好的搞一搞仍是頗有必要的。參數列表也有不少好用的使用方式,接下來詳細的介紹一下函數的形參列表。

(1) 默認的形參是常量(let)

在函數的形參列表中,默認的形參是常量。也就是至關於用let關鍵字對形參進行修飾了。咱們能夠作個試驗,把上面加法函數作一個修改,在加法函數中對number1進行加1操做,你會獲得一個錯誤,這個錯誤的大致意思就是「number1是不可被修改的,由於它是let類型的常量」。而且編譯器還給人出了Fix-it(修復)的方案,就是在number1前面使用var關鍵字進行修飾,使其成爲變量,這樣才能夠修改其值。

上面說這麼多,一句話:形參默認是常量,若是你想讓其是變量,那麼你可使用var關鍵字進行修飾,這樣被關鍵字var修飾的變量在函數中就能夠被修改。下方就是報的這個錯誤,和編譯器提供的解決方案。(在Objc中默承認以在函數中改變形參的值)

 

(2)給形參命名

爲了代碼的可讀性和可維護性,咱們在定義函數時,須要爲每一個參數名一個名字,這樣調用者見名知意,很容易就知道這個參數表明什麼意思了。接下來仍是在上述加法函數中進行修改,爲每一個參數名一個名字,並看一下調用方式。修改上面的函數,給第一個形參命名成numberOne, 第二個形參爲numberTwo, 下方是修改後的函數。 緊接着sum()函數的調用方式也會有所改變,在調用函數時編譯器會給出參數的名稱,這樣調用者一目瞭然。

1

2

3

4

5

6

//函數定義

func sum (numberOne number1:Int, numberTwo number2:Int) -> Int{

    return number1 + number2;

}

 

let sumTwoNubmer = sum(numberOne: 10, numberTwo: 20);

調用上述函數時,下方是編譯器給出的提示,一目瞭然呢。

關於Swift中參數名的內容,要說明的是在Swift1.0的時候,你能夠在參數前面添加上#號,而後參數名就與變量(或者常量)的名字相同,而Swift2.0後這個東西去掉了,由於默認就至關於Swift1.0中添加#號。

 

(3) 函數的傳參與傳引用

先暫且這麼說着,在C語言的函數中能夠給函數傳入參數,或者傳入實參的內存地址就是所謂的傳引用。若是傳入的是引用的話,在函數中對值進行修改的話,那麼出了函數,這個被修改的值是能夠被保留的。在Swift中也是能夠的,不過你須要使用inout關鍵字修飾形參,而且在使用該函數時,用&來修飾。這一點和C語言中相似,&就是取地址符。下方是inout使用的一個小實例。

1

2

3

4

5

 func incrementStepTwo (inout myNumber:Int) {

     myNumber += 2

 }

 var myTestNumber = 6

 incrementStepTow(&myTestNumber)  //myTestNumber = 8

myTestNumber變量通過incrementStepTwo()函數後,其值就會增長2。固然前提是myTestNumber是變量,若是myTestNumber是常量的話,那麼對不起,調用該函數就會報錯,下面是把var改爲let後IDE給的錯誤提示。錯誤緣由很顯然是你動了一個不應動的值,也就是常量不可再次被修改的。

 

    

(4) 不定參數函數

不定參數函數也就是形參的個數是不定的,可是形參的類型必須是相同的。不定形參在使用時怎麼取呢?不定個數的形參其實是一個數組,咱們能夠經過for循環的形式來遍歷出每一個形參的值,而後使用就能夠了。下方incrementMultableAdd()函數的形參的個數是不定的,其功能是求多個整數的和。在函數中咱們只需遍歷每一個參數,而後把每一個參數進行相加,最後返回所求的和便可。函數比較簡單,再此就不在囉嗦了。

 

(5) 默認形參值

在Swift語言中是支持給形參賦初始值的,這一點在其餘一些編程語言中也是支持的。可是Objective-C這麼看似古老的語言中就不支持給形參指定初始值,在Swift這門現代編程語言中是支持這一特性的。默認參數要從參數列表後開始爲參數指定默認值,否則就會報錯。下方就是爲函數的形參指定默認參數的示例。一個表白的方法sayLove(), 形參youName默認是「山伯」, 形參loverName默認是「英臺」。 緊接着是sayLove函數的三種不一樣的調用方式,在調用函數時你能夠不傳參數,能夠傳一個參數,固然傳兩個也是沒問題的。

 

由於函數的每一個參數都是有名字的,在含有默認參數的函數調用時,能夠給任意一個參數進行傳值,其餘參數取默認值,這也是Swift的一大特點之一,具體請看以下簡單的代碼示例:

 

3.函數類型

每一個函數都有本身的所屬類型,函數類型說白了就是若是兩個函數參數列表相同以及返回值類型相同,那麼這兩個函數就有着相同的函數類型。在Swift中能夠定義一個變量或者常量來存儲一個函數的類型。接下來將用過一個實例還介紹一下函數類型是個什麼東西。

(1) 首先建立兩個函數類型相同的函數,一個函數返回兩個整數的差值,另外一個函數返回兩個整數的乘積。固然這兩個函數比較簡單,直接上代碼:

1

2

3

4

5

6

7

8

//現定義兩個函數類型相同的函數

func diff (number1:Int, number2:Int) -> Int {

    return number1 - number2;

}

 

func mul (number1:Int, number2:Int) -> Int {

    return number1 * number2;

}

(2) 函數定義好後,接着要定義個一個枚舉來枚舉每種函數的類型,下面定義這個枚舉在選擇函數時會用到,枚舉定義以下:

1

2

3

4

5

 //定義兩種計算的枚舉類型

 enum CountType:Int {

     case DiffCount = 0

     case MulCount

 }

(3) 接下來就是把(1)和(2)中定義的東西經過一個函數來組合起來。說白了,就是定義個函數來經過枚舉值返回這個枚舉值所對應的函數類型。有時候說多了容易犯迷糊,就直接上代碼得了。下方函數的功能就是根據傳進來的枚舉值來返回相應的函數類型。

1

2

3

4

5

6

7

8

9

10

11

12

13

//選擇類型的函數,並返回相應的函數類型

func choiseCountType(countType:CountType) -> ((Int, Int) -> Int) {

    //函數類型變量

    var myFuncType:(Int, Int) -> Int

     

    switch countType {

    case .DiffCount:

        myFuncType = diff

    case .MulCount:

        myFuncType = mul

    }

    return myFuncType;

}

(4) 接下來就是使用(3)中定義的函數了,首先咱們須要定義一個相應函數類型((Int, Int) -> Int)的變量來接收choiseCountType()函數中返回的函數類型,而後調用該函數類型變量,在Playground中執行的結果以下:

 

4.函數嵌套

咱們能夠把 3 中的代碼使用函數嵌套進行重寫,在Swift中是支持函數嵌套的。 因此能夠吧3.1和3.2中的函數放到3.3函數中的,因此咱們能夠對上述代碼使用函數嵌套進行重寫。使用函數嵌套重寫後的代碼以下所示,固然,choiseCountType()函數的調用方式沒用發生改變,重寫後的調用方式和3.4中的調用方式是同樣同樣的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

//選擇類型的函數,並返回相應的函數類型

func choiseCountType(countType:CountType) -> ((Int, Int) -> Int) {

     

    //現定義兩個函數類型相同的函數

    func diff (number1:Int, number2:Int) -> Int {

        return number1 - number2;

    }

     

    func mul (number1:Int, number2:Int) -> Int {

        return number1 * number2;

    }

 

     

    //函數類型變量

    var myFuncType:(Int, Int) -> Int

     

    switch countType {

    case .DiffCount:

        myFuncType = diff

    case .MulCount:

        myFuncType = mul

    }

    return myFuncType;

}

 

二. 閉包

說道Swift中的閉包呢,不得不提的就是Objective-C中的Block, 其實二者是一個東西,使用方式以及使用場景都是相同的。咱們徹底能夠類比着Objective-C中的Block來介紹一下Swift中的Closure(閉包)。其實就是匿名函數。接下來的這段內容,先介紹一下Swift中Closure的基本語法,而後在類比着ObjC中的Block窺探一下Closure的使用場景。

1.Closure變量的聲明

Closure就是匿名函數,咱們能夠定義一個閉包變量,而這個閉包變量的類型就是咱們上面介紹的「函數類型」。定義一個閉包變量其實就是定義一個特定函數類型的變量,方式以下。由於Closure變量沒有賦初始值,因此咱們把其聲明爲可選類型的變量。在使用時,用!強制打開便可。

1

var myCloure0:((Int, Int) -> Int)?

除了上面的方式外,咱們還用另外一種經常使用的聲明閉包變量的方式。那就是使用關鍵字typealias定義一個特定函數類型,咱們就能夠拿着這個類型去聲明一個Closure變量了,以下所示

1

2

3

 //定義閉包類型 (就是一個函數類型)

 typealias MyClosureType = (Int, Int) -> Int

 var myCloure:MyClosureType?

 

2. 給Closure變量賦值

給Closure變量賦值,其實就是把一個函數體賦值給一個函數類型的變量,和函數的定義區別不大。可是給閉包變量賦值的函數體中含有參數列表,而且參數列表和真正的函數體之間使用關鍵字in來分割。 閉包可選變量的調用方式與普通函數沒什麼兩樣,惟一不一樣的是這個函數須要用!來強制打開纔可使用。賦值和調用方式以下。

 

3. 閉包回調的應用實例

暫且先稱做閉包回調吧,其實就是Objc中的Block回調。在Swift中的閉包回調和Objc中的Block回調用法一致,下方將會經過一個實例來介紹一下閉包的應用之一。下方會建立兩個視圖控制器,咱們暫且稱爲FirstViewController和SecondViewController。在FirstViewController上有一個Label和一個Button, 這個Button用來跳轉到SecondViewController, 而這個Label用來顯示從SecondViewController中回調過來的值。 而SecondViewController也有一個TextField和一個Button, 點擊Button就會把輸入框中的值經過閉包回調回傳到FirstViewController而後在FirstViewController上的Label顯示。

(1) 構建這個實例的第一步要作的就是使用Stroyboard把咱們所需的控件佈局好,而且管理相應的類。固然咱們這個Demo的重點不在於如何去佈局控件,如何去關聯控件,以及如何去使用控件,因此上述的這些就不作贅述了。這個實例的重點在於如何使用Closure實現值的回調。下方是咱們的控件佈局和目錄結構的截圖,從Storyboard上的控件來看,功能也就一目瞭然了。點擊「FirstViewController」 上的「Go SecondViewController」按鈕,就會跳轉到 「SecondViewController」 。 在SecondViewController視圖上的輸入框輸入數值,點擊Back按鈕返回到FirstViewController, 同時把輸入框中的文本經過閉包回調的形式回傳過來在FristViewController的label上顯示。大體就這個簡單的功能。

 

(2)FirstViewController.swift中的內容

FirstViewController.swift中的內容比較簡單,就關聯一個Label控件和一個按鈕點擊的事件,點擊按鈕就會跳轉到SecondViewController,具體代碼以下,在此就不囉嗦了,請看代碼中的註釋。下方代碼重要的一點是在跳轉到SecondViewController時要實現其提供的閉包回調,以便接受回傳過來的值。

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 {

 

    @IBOutlet var showTextLabel: UILabel!       //展現回調過來的文字信息

     

    override func viewDidLoad() {

        super.viewDidLoad()

    }

 

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

     

    //點擊按鈕跳轉到SecondViewController

    @IBAction func tapGoSecondViewControllerButton(sender: UIButton) {

        //從Storyboard上加載SecondViewController

        let secondVC = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("SecondViewController")as! SecondViewController

         

        //實現回調,接收回調過來的值

        secondVC.setBackMyClosure { (inputText:String) -> Void in

            self.showTextLabel.text = inputText

        }

         

        //push到SecondViewController

        self.navigationController?.pushViewController(secondVC, animated: true)

    }

}

 

(3) SecondViewController.swift中的內容

SecondViewController.swift中的內容也不麻煩,就是除了關聯控件和事件外,還定義了一個閉包類型(函數類型),而後使用這個特定的函數類型聲明瞭一個此函數類型對應的變量。咱們能夠經過這個變量來接受上個頁面傳過來的閉包體,從而把用戶輸入的值,經過這個閉包體回傳到上個頁面。具體代碼實現以下:

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   //定義閉包類型(特定的函數類型函數類型)

 

class SecondViewController: UIViewController {

     

    @IBOutlet var inputTextField: UITextField!  //輸入框,讓用戶輸入值,而後經過閉包回調到上一個頁面

     

    var backClosure:InputClosureType?           //接收上個頁面穿過來的閉包塊

     

    override func viewDidLoad() {

        super.viewDidLoad()

    }

 

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

     

    //閉包變量的Seter方法

    func setBackMyClosure(tempClosure:InputClosureType) {

        self.backClosure = tempClosure

    }

     

    @IBAction func tapBackButton(sender: UIButton) {

        if self.backClosure != nil {

            let tempString:String? = self.inputTextField.text

            if tempString != nil {

                self.backClosure!(tempString!)

            }

        }

        self.navigationController!.popViewControllerAnimated(true)

    }

}

 

(4) 通過上面的步驟這個實例已經完成,接下來就是看一下運行效果的時間了。原本想作成Git動態圖的,感受實例功能簡單,並且UI上也比較簡單,就沒作,仍是看截圖吧。運行效果的截圖以下:

4.數組中經常使用的閉包函數

在Swift的數組中自帶了一些比較好用的閉包函數,例如Map, Filter, Reduce。接下來就好好的看一下這些閉包,用起來仍是比較爽的。

(1) Map(映射)

說到Map的用法和功能,不能不說的是若是你使用過ReactiveCocoa框架,那麼對裏邊的Sequence中的Map的使用方式並不陌生。其實二者的使用方法和功能是極爲類似的。若是你沒使用過RAC中的Map,那也可有可無,接下來咱們先上段代碼開看一下數組中的Map閉包函數。

經過上面的代碼段以及運行結果,咱們不難看出,map閉包函數的功能就是對數組中的每一項進行遍歷,而後經過映射規則對數組中的每一項進行處理,最終的返回結果是處理後的數組(以一個新的數組形式出現)。固然,原來數組中的元素值是保持不變的,這就是map閉包函數的用法與功能。 

 

(2) Filter (過濾器)

Filter的用法仍是比較好理解的,Filter就是一個漏勺,就是用來過濾符合條件的數據的。在ReactiveCocoa中的Sequence也是有Filter的,用法仍是來過濾Sequence中的數據的。而在數組中的Filter用來過濾數組中的數據,而且返回新的數組,新的數組中存放的就是符合條件的數據。Filter的用法以下實例,下方的實例就是一個身高的過濾,過濾掉身高小於173的人,返回大於等於173的身高數據。

(3)Reduce 

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

相關文章
相關標籤/搜索