iOS學習筆記42 Swift(二)函式和閉包

執著丶執念發表於2018-06-02

上一節我們講了Swift的基礎部分,例如資料型別、運算子和控制流等,現在我們來看下Swift的函式和閉包 ###一、Swift函式 函式是一個完成獨立任務的程式碼塊,Swift中的函式不僅可以像C語言中的函式一樣作為函式的引數和返回值,而且還支援巢狀,支援函式引數預設值、可變引數等。

/**
* 1、函式第一個引數預設沒有外部引數名,其他引數預設有
* 2、可變引數只能在最後一個引數,可變引數的型別是陣列
* 3、返回型別也可以是元組
* 4、可以在引數前面加inout關鍵字,表示內部修改會改變外部的變數,呼叫時要加“&”符號
* 5、Swift中的函式本身也可以看做一種型別,既可以作為引數又可以作為返回值。
     例如 var fun:(Int,Int)->(Double,Int) = fun2
*/
func 函式名(引數1:型別1, inout 引數2:型別2=預設值2, 可變引數3:型別3...) -> 返回值型別 {

	函式體

    return 返回值
}
複製程式碼

#####函式例項: ######1. 普通函式

//1. 定義一個函式,注意引數和返回值,如果沒有返回值可以不寫返回值或者寫成Void、空元組()
func mySum(num1:Int, num2:Int) -> Int{
    return num1 + num2
}
//呼叫函式,num2是外部引數名,函式第一個引數預設沒有外部引數名,其他引數預設有
mySum(1, num2: 2)
複製程式碼

######2. 設定外部引數名

/**
*  2. 函式引數名分為區域性引數名和外部引數名
*/
func mySplit(string a:String, seperator b:Character) -> [String]{
    return ["hello", "world", "!"]
}
/* 
由於給mySplit函式設定了外部引數名string和seperator,所以執行的時候必須帶上外部引數名,
此處可以看到一個有意義的外部引數名大大節省開發者使用成本
 */
mySplit(string: "hello,world,!", seperator: ",") //結果:["hello", "world", "!"]
複製程式碼

######3. 設定預設引數值

//3. 設定函式的最後一個引數預設值設定為",",注意如果使用預設引數那麼此引數名將預設作為外部引數名
func mySplit3(string:String, seperator:Character=",")->[String]{
    return ["hello", "world", "!"]
}
mySplit3("hello,world,!") //結果:["hello", "world", "!"]
mySplit3("hello world !", seperator: " ") //結果:["hello", "world", "!"]
複製程式碼

######4. 設定可變引數

/**
 * 4. 可變引數,一個函式最多有一個可變引數並且作為最後一個引數
 * 下面strings引數在內部是一個[String],對於外部是不定個數的String引數
 */
func myJoinStr(seperator seperator:Character=",", strings:String...) -> String{
    var result:String = ""
    for var i = 0;i < strings.count; ++i{
        if i != 0{
            result.append(seperator)
        }
        result += strings[i]
    }
    return result
}
//呼叫
myJoinStr(seperator:" ", strings: "hello","world","!") //結果:"hello world !"
複製程式碼

######5. 設定輸入輸出引數

/**
 *  5. 輸入輸出引數
 *  通過輸入輸出引數可以在函式內部修改函式外部的變數(注意呼叫時不能是常量或字面量)
 *  注意:下面的mySwap僅僅為了演示,實際使用時請用Swift的全域性函式swap
 */
func mySwap(inout a:Int ,inout b:Int){
    a = a + b
    b = a - b
    a = a - b
}
var a = 1,b = 2
mySwap(&a, b: &b) //呼叫時引數加上“&”符號
print("a=\(a),b=\(b)") //結果:"a=2,b=1"
複製程式碼

######6. 函式型別使用

/**
 * 6. 函式型別
 */
var sum3 = mySum //自動推斷sum3的型別:(Int,Int)->Int,注意不同的函式型別之間不能直接賦值
sum3(1,num2: 2) //結果:3

//函式作為返回值
func fn() -> (Int,Int)->Int{
    //下面的函式是一個巢狀函式,作用於是在fn函式內部
    func minus(a:Int, b:Int) -> Int{
        return a - b
    }
    return minus;
}
var minus = fn()
minus(1,2) //結果:-1

//函式作為引數
func caculate(num1:Int,num2:Int,fn:(Int,Int)->Int) -> Int{
    return fn(num1,num2)
}
caculate(1,num2: 2,fn: mySum) //結果:3
caculate(1,num2: 2,fn: minus) //結果:-1
複製程式碼

###二、閉包 Swift中的閉包其實就是一個函式程式碼塊,它和ObjC中的Block及Java中的lambda是類似的。 閉包的特點就是可以捕獲和儲存上下文中的常量或者變數的引用,即使這些常量或者變數在原作用域已經被銷燬了在程式碼塊中仍然可以使用。 ######在Swift中閉包表示式的定義形式如下:

{ ( parameters ) -> returnType in 
    statements;
}
複製程式碼

#####閉包使用: ######1. 不使用閉包,使用函式

func mySum(num1:Int,num2:Int) -> Int{
    return num1 + num2
}
func myMinus(num1:Int,num2:Int) -> Int{
    return num1 - num2
}
func myCaculate(num1:Int, num2:Int, fn:(Int,Int)->Int) -> Int{
    return fn(num1,num2)
}
var (a, b) = (1, 2)
myCaculate(a, num2: b, fn: mySum) //結果:3
myCaculate(a, num2: b, fn: myMinus) //結果:-1
複製程式碼

######2. 使用閉包

//利用閉包表示式替代函式mySum
myCaculate(a, num2: b, fn: {(num1:Int, num2:Int) -> Int in
    return num1 + num2
}) //結果:3
//利用閉包表示式替代函式myMinus
myCaculate(a, num2: b, fn: {(num1:Int, num2:Int) -> Int in
    return num1 - num2
}) //結果:-1
複製程式碼

######3. 閉包的簡化形式

//簡化形式,根據上下文推斷型別並且對於單表示式閉包(只有一個語句)可以隱藏return關鍵字
myCaculate(a, num2: b, fn: { num1, num2 in
    num1 + num2
}) //結果:3
myCaculate(a, num2: b, fn: { num1, num2 in
    num1 - num2
}) //結果:-1
複製程式碼

######4. 閉包繼續簡化,使用引數縮寫

//再次簡化,使用引數名縮寫,使用$0...$n代表第n個引數,並且此in關鍵字也省略了
myCaculate(a, num2: b, fn: { $0 + $1 }) //結果:3
myCaculate(a, num2: b, fn: { $0 - $1 }) //結果:-1
複製程式碼

考慮到閉包表示式的可讀取性,Swift中如果一個函式的最後一個引數是一個函式型別的引數(或者說是閉包表示式),則可以將此引數寫在函式括號之後,這種閉包稱之為**“尾隨閉包”**。 ######5. 尾隨閉包

//尾隨閉包
myCaculate(a,num2: b) { 
    $0 + $1
} //結果:3
myCaculate(a,num2: b) { 
    $0 - $1 
} //結果:-1
複製程式碼

######6. 捕獲變數 前面說過閉包之所以稱之為“閉包”,就是因為其可以捕獲一定作用域內的常量或者變數進而閉合幷包裹著。

func myAdd() -> ()->Int {
    var total = 0
    var step = 1
    func fn() -> Int{
        total += step
        return total
    }
    return fn
}
/* 
fn捕獲了total和step,儘管下面的myAdd()執行完後total和step被釋放,
但是由於fn捕獲了二者的副本,所以fn會隨著兩個變數的副本一起被儲存
 */
var a = myAdd()
a() //結果:1
a() //結果:2,說明a中儲存了total的副本(否則結果會是1)
 
var b = myAdd()
b() //結果:1,說明a和b單獨儲存了total的副本(否則結果會是3)
 
var c = b
c() //結果:2,說明閉包是引用型別,換句話說函式是引用型別(否則結果會是1)
複製程式碼
  • Swift會自動決定捕獲變數或者常量副本的拷貝型別(值拷貝或者引用拷貝)而不需要開發者關心
  • 被捕獲的變數或者常量的記憶體管理同樣是由Swift來管理,我們不用關心,例如當上面的函式a不再使用了,那麼fn捕獲的兩個變數也就釋放了。

iOS學習筆記42 Swift(二)函式和閉包

#####有什麼問題在下方評論區中提出!O(∩_∩)O哈!

相關文章