在 Swift 中的閉包類似於結構塊,並可以在任何地方調用,它就像 C 和 Objective C 語言內建的函數。 函數內部定義的常數和變數引用可被捕獲並儲存在閉包。函數被視為封閉的特殊情況,它有 3 種形式。
在 Swift 語言閉合運算式,如下最佳化,重量輕文法風格,其中包括:
- 推導參數並從操作功能表傳回值的類型
- 從單封表達的隱性返回
- 簡略參數名稱
- 尾部閉包文法
文法
下面是一個通用的文法定義用於閉包,它接受參數並返回資料的類型:
複製代碼 代碼如下:
{(parameters) -> return type in
statements
}
下面是一個簡單的例子:
複製代碼 代碼如下:
let studname = { println("Welcome to Swift Closures") }
studname()
當我們使用 playground 運行上面的程式,我們得到以下結果
Welcome to Swift Closures
以下閉包接受兩個參數並返回一個布爾值:
複製代碼 代碼如下:
{(Int, Int) -> Bool in
Statement1
Statement 2
---
Statement n
}
下面是一個簡單的例子:
複製代碼 代碼如下:
let divide = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
let result = divide(200, 20)
println (result)
當我們使用 playground 運行上面的程式,我們得到以下結果
在閉包中的運算式
以便捷的方式命名來定義代碼塊可以通過嵌套函數實現的。取而代之代表整個函式宣告及名稱構造用來表示函數。代表函數的文法清晰,簡短聲明是通過封閉的表達來實現的。
升序排列程式
排序字串是 Swift 中保留的函數 “sorted”,這是在標準庫中已提供實現。該函數將所述給定的字串進行遞增順序排序並返回具有相同的尺寸,並在舊數組中相同資料類型的一個新的數組的元素。舊的數組保持不變。
兩個參數的排序在函數內部表示:
已知類型的值表示為數組
數組的內容 (Int,Int) ,並返回一個布爾值(Bool),如果數組排序不好就會返回true,否則將返回false。
普通函數帶輸入字串被寫入,並傳遞給排序函數獲得字元到新的數組,如下面所示:
複製代碼 代碼如下:
func ascend(s1: String, s2: String) -> Bool {
return s1 > s2
}
let stringcmp = ascend("swift", "great")
println (stringcmp)
當我們使用 playground 運行上面的程式,我們得到以下結果
複製代碼 代碼如下:
最初的數組排序給定為 "Swift" 和 "great"。函數用來數組排序被聲明為字串資料型別,並且傳回型別為布爾型。 兩個字串進行比較,並以升序排序,並儲存在新的數組。如果排序執行成功,該函數將返回true;否則將返回 false。
閉包運算式文法用法
常量參數:
可變參數 和 inout 參數
閉包表達不支援的預設值。可變參數和參數元組也可以用來作為參數類型和傳回型別。
複製代碼 代碼如下:
let sum = {(no1: Int, no2: Int) -> Int in
return no1 + no2
}
let digits = sum(10, 20)
println(digits)
當我們使用 playground 運行上面的程式,我們得到以下結果
在函式宣告中提到的參數和傳回型別聲明,也可通過使用 'in' 關鍵字內聯閉包運算式函數表示。 一旦聲明參數及其傳回型別“in”關鍵字,則用於表示閉包體。
單一運算式隱式返回
在這裡,排序函數的第二個參數的函數類型明確指出,一個布爾值必須由閉包返回。因為閉包體內含有一個運算式(s1 > s2)返回一個布爾值, 不會出現歧義,其返回關鍵字可以省略。
要返回一個運算式語句在閉包中, “return” 關鍵字在其聲明部分被省略。
複製代碼 代碼如下:
let count = [5, 10, -6, 75, 20]
var descending = sorted(count, { n1, n2 in n1 > n2 })
var ascending = sorted(count, { n1, n2 in n1 < n2 })
println(descending)
println(ascending)
當我們使用 playground 運行上面的程式,我們得到以下結果
[75, 20, 10, 5, -6][-6, 5, 10, 20, 75]
該語句本身明確規定,當 string1 大於 string2 返回 true,否則為false,因此return語句省略。
已知類型的閉包
考慮兩個數相加。我們知道相加後將返回整數資料類型。因此,已知類型的閉包聲明
複製代碼 代碼如下:
let sub = {(no1: Int, no2: Int) -> Int in
return no1 - no2
}
let digits = sub(10, 20)
println(digits)
當我們使用 playground 運行上面的程式,我們得到以下結果
聲明簡寫參數名稱作為閉包
Swift 自動提供簡寫參數名內聯閉包, 可以使用由 $0,$1,$2 等等名稱,指的是封閉的參數值。
複製代碼 代碼如下:
var shorthand: (String, String) -> String
shorthand = { $1 }
println(shorthand("100", "200"))
在這裡,$0 和 $1 參考閉包的第一和第二個字串參數。
當我們使用 playground 運行上面的程式,我們得到以下結果
Swift 方便使用者來表示內嵌閉包為縮寫參數名為:$0, $1, $2 --- $n.
閉包參數列表中被省略定義部分,當我們表示內部閉包運算式簡寫參數名。 根據函數類型簡寫參數名稱將被匯出。由於簡寫參數表達體所定義的 'in' 關鍵字被省略。
閉包作為操作函數
Swift 提供了一種簡單的方法訪問的成員,只需提供操作符函數作為閉包。 在前面的例子關鍵字“Bool”是用來比較兩個字串,相等返回“true”,否則返回“false”。
運算式即使在閉包中變得簡單在操作函數:
複製代碼 代碼如下:
let numb = [98, -20, -30, 42, 18, 35]
var sortedNumbers = numb.sorted({
(left: Int, right: Int) -> Bool in
return left < right
})
let asc = numb.sorted(<)
println(asc)
當我們使用 playground 運行上面的程式,我們得到以下結果
[-30, -20, 18, 35, 42, 98]
閉包作為尾隨包
傳遞這個函數的最後一個參數到閉合運算式使用“尾隨閉包”聲明。它使用 {} 寫在函數()外部。當它不能寫入函數內聯在一行上,使用它是需要。
複製代碼 代碼如下:
reversed = sorted(names) { $0 > $1}
其中 {$0 > $1} 表示為外部(名稱)聲明尾隨閉包。
複製代碼 代碼如下:
import Foundation
var letters = ["North", "East", "West", "South"]
let twoletters = letters.map({ (state: String) -> String in
return state.substringToIndex(advance(state.startIndex, 2)).uppercaseString
})
let stletters = letters.map() { $0.substringToIndex(advance($0.startIndex, 2)).uppercaseString }
println(stletters)
當我們使用 playground 運行上面的程式,我們得到以下結果
捕獲值和參考型別
在閉包的協助下 Swift 完成捕捉常量和變數的值。它還參考修改值,即使常量和變數在閉包體已經不存。
捕獲常數和變數值是通過使用嵌套函數寫入函數,這是使用其它函數體來實現的:
- 一個嵌套函數捕獲
- 外部函數參數
- 捕捉常量和外部函數中定義的變數
Swift 中當常量或變數在函數中聲明,引用到變數也自動地被閉合建立。它也提供工具來引用兩個以上的變數作為同一閉合如下:
複製代碼 代碼如下:
let decrem = calcDecrement(forDecrement: 18)
decrem()
在這裡,oneDecrement 和 遞減變數都指向同一個記憶體塊閉合參考。
複製代碼 代碼如下:
func calcDecrement(forDecrement total: Int) -> () -> Int {
var overallDecrement = 100
func decrementer() -> Int {
overallDecrement -= total
println(overallDecrement)
return overallDecrement
}
return decrementer
}
let decrem = calcDecrement(forDecrement: 18)
decrem()
decrem()
decrem()
當我們使用 playground 運行上面的程式,我們得到以下結果:
當每一個外部函數 calcDecrement 調用時都會調用 decrementer()函數 並通過值 18 遞減,並在外部函數 calcDecrement 的協助下返回結果。在這裡,calcDecrement 作為一個閉合。
即使函數 decrement()沒有任何參數,閉合預設情況下是指變數的"整體遞減“ “total” 通過擷取其值。為指定的變數的值副本被使用新的 decrementer()函數儲存。Swift 通過處理儲存空間管理功能分配和釋放儲存空間空間當變數在不使用。
下面來一波總結~
複製代碼 代碼如下:
/* 閉包(Closures)
* 閉包是自包含的功能代碼塊,可以在代碼中使用或者用來作為參數傳值。
* 在Swift中的閉包與C、OC中的blocks和其它程式設計語言(如Python)中的lambdas類似。
* 閉包可以捕獲和儲存上下文中定義的的任何常量和變數的引用。這就是所謂的變數和變數的自封閉,
* 因此命名為”閉包“("Closures)").Swift還會處理所有捕獲的引用的記憶體管理。
*
* 全域函數和嵌套函數其實就是特殊的閉包。
* 閉包的形式有:
* (1)全域函數都是閉包,有名字但不能捕獲任何值。
* (2)嵌套函數都是閉包,且有名字,也能捕獲封閉函數內的值。
* (3)閉包運算式都是無名閉包,使用輕量級文法,可以根據上下文環境捕獲值。
*
* Swift中的閉包有很多最佳化的地方:
* (1)根據上下文推斷參數和傳回值類型
* (2)從單行運算式閉包中隱式返回(也就是閉包體只有一行代碼,可以省略return)
* (3)可以使用簡化參數名,如$0, $1(從0開始,表示第i個參數...)
* (4)提供了尾隨閉包文法(Trailing closure syntax)
*/
// 下面用Swift標準庫中的sort方法來一步步簡化閉包寫法
// sort函數需要兩個參數
// 參數一:數組
// 參數二:一個閉包:帶有兩個參數,這兩個參數類型與數組中的元素類型相同,傳回值是Bool
var names = ["Swift", "Arial", "Soga", "Donary"]
// 第一種方式:使用函數
func backwards(firstString: String, secondString: String) -> Bool {
return firstString > secondString // 升序排序
}
// 這裡第二個參數,傳了一個函數
// reversed is equal to ["Swift", "Soga", "Donary", "Arial"]
var reversed = sort(nams, backwards)
// 第二種方式:使用閉包方式
// 完整閉包寫法是在花括弧內有參數列表和傳回值,用關鍵字in表明閉包體的開始
// (firstString: String, secondString: String) 閉包參數列表
// -> Bool 指明閉包傳回值類型是Bool
// in關鍵字表明閉包體的開始
reversed = sort(names, { (firstString: String, secondString: String) -> Bool in
return firstString > secondString
})
// 這裡可以進一步簡化寫法,因為閉包代碼比較短,可以寫到一行上
reversed = sort(names, { (firstString: String, secondString: String) -> Bool in return firstString > secondString})
// 下面再進一步簡化寫法 :根據環境內容自動推斷出類型
// 參數列表都沒有指明類型,也沒有指明傳回值類型,這是因為swift可以根據上下文推測出
// firstString和secondString的類型會是names數組元素的類型,而傳回值類型會根據return語句結果得到
reversed = sort(names, { firstString, secondString in return firstString > secondString})
// 再進一步簡化:隱式返回(單行語句閉包)
// 因為閉包體只有一行代碼,可以省略return
reversed = sort(names, { firstString, secondString in firstString > secondString})
// 再進一步簡化:使用簡化參數名($i,i=0,1,2...從0開始的)
// Swift會推斷出閉包需要兩個參數,類型與names數組元素相同
reversed = sort(names, { $0 > $1 })
// 最簡單的一種寫法:使用操作符
reversed = sort(names, >)
/*
* 尾隨閉包(Trailing Closures)
* 如果函數需要一個閉包參數作為參數,且這個參數是最後一個參數,而這個閉包運算式又很長時,
* 使用尾隨閉包是很有用的。尾隨閉包可以放在函數參數列表外,也就是括弧外。如果函數只有一個參數,
* 那麼可以把括弧()省略掉,後面直接跟著閉包。
*/
// Array的方法map()就需要一個閉包作為參數
let strings = numbers.map { // map函數後面的()可以省略掉
(var number) -> String in
var output = ""
while number > 0 {
output = String(number % 10) + output
number /= 10
}
return output
}
/* 捕獲值
* 閉包可以根據環境內容捕獲到定義的常量和變數。閉包可以引用和修改這些捕獲到的常量和變數,
* 就算在原來的範圍內定義為常量或者變數已經不再存在(很牛逼)。
* 在Swift中閉包的最簡單形式是嵌套函數。
*/
func increment(#amount: Int) -> (() -> Int) {
var total = 0
func incrementAmount() -> Int {
total += amount // total是外部函數體內的變數,這裡是可以捕獲到的
return total
}
return incrementAmount // 返回的是一個嵌套函數(閉包)
}
// 閉包是參考型別,所以incrementByTen聲明為常量也可以修改total
let incrementByTen = increment(amount: 10)
incrementByTen() // return 10,incrementByTen是一個閉包
// 這裡是沒有改變對increment的引用,所以會儲存之前的值
incrementByTen() // return 20
incrementByTen() // return 30
let incrementByOne = increment(amount: 1)
incrementByOne() // return 1
incrementByOne() // return 2
incrementByTen() // return 40
incrementByOne() // return 3