- 閉包是自包含的功能代碼塊,可以在代碼中使用或者用來作為參數(shù)傳值。
- 在Swift中的閉包與OC中的block類似。
//閉包的表達(dá)式語法
/*
{(parameters) ->(return type) in
statements
}
*/
//對(duì)整數(shù)進(jìn)行排序 sorted 后面就是閉包
let numbers = [1, 5, 9, 6, 2, 3, 4]
let newNumbers = numbers.sorted { (a, b) -> Bool in
return a > b
}
print(newNumbers) //結(jié)果是 [9, 6, 5, 4, 3, 2, 1]
- 全局函數(shù)和嵌套函數(shù)其實(shí)就是特殊的閉包雳窟。
//設(shè)置全局方法獲取某些值
func kNaviBarHeight() -> Float {
return 44
}
print(kNaviBarHeight())
- 閉包的形式有:
- (1)全局函數(shù)都是閉包,有名字但不能捕獲任何值。
- (2)嵌套函數(shù)都是閉包封救,且有名字拇涤,也能捕獲封閉函數(shù)內(nèi)的值。
- (3)閉包表達(dá)式都是無名閉包誉结,使用輕量級(jí)語法鹅士,可以根據(jù)上下文環(huán)境捕獲值。
- Swift中的閉包有很多優(yōu)化的地方:
- (1)根據(jù)上下文推斷參數(shù)和返回值類型
- (2)從單行表達(dá)式閉包中隱式返回(也就是閉包體只有一行代碼惩坑,可以省略return)
- (3)可以使用簡化參數(shù)名掉盅,如$0, $1(從0開始,表示第i個(gè)參數(shù)...)
- (4)提供了尾隨閉包語法(Trailing closure syntax)
*/
// 以下閉包以Array的sorted函數(shù)為例
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ str1: String, _ str2: String) -> Bool {
return str1 > str2
}
// sorted函數(shù)的參數(shù)是一個(gè)閉包以舒,下面?zhèn)髁艘粋€(gè)方法名趾痘,由此說明:嵌套函數(shù)是一個(gè)有名字但并可以捕獲其封閉函數(shù)域內(nèi)值的閉包
var reversedNames = names.sorted(by: backward)
// 普通閉包格式:(參數(shù): 參數(shù)類型,...) -> 返回值類型 in ...
reversedNames = names.sorted(by: { (str1: String, str2: String) -> Bool in
return str1 > str2
})
// 根據(jù)Swift的類型推斷,參數(shù)類型及參數(shù)括號(hào)可以去掉蔓钟,返回值類型可以去掉
reversedNames = names.sorted(by: { str1, str2 in
return str1 > str2
})
// 單行表達(dá)式:可以去掉return
reversedNames = names.sorted(by: {str1, str2 in
str1 > str2
})
// 使用參數(shù)名縮寫:參數(shù)和in也可以去掉
reversedNames = names.sorted(by: {
$0 > $1 //$0代表第1個(gè)參數(shù)永票, $1 代表第二個(gè)參數(shù)
})
// 使用運(yùn)算符:因?yàn)镾wift中為字符串重載了大于號(hào)小于號(hào)
reversedNames = names.sorted(by: >)
// 使用尾隨閉包:前提是閉包必須是函數(shù)的最后一個(gè)參數(shù)
reversedNames = names.sorted() {$0 < $1}
// 使用尾隨閉包:閉包是函數(shù)唯一參數(shù)時(shí),可以省掉參數(shù)括號(hào)
reversedNames = names.sorted {$0 < $1}
print(reversedNames)
- 閉包值捕獲滥沫,閉包是引用類型
//sum 返回值是一個(gè)閉包 () -> Int
func sum(amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
print("runningTotal = \(runningTotal)")
runningTotal += amount
return runningTotal
}
return incrementer
}
let addByTen = sum(amount: 10)
print("結(jié)果 \(addByTen())")
print("結(jié)果 \(addByTen())")
//打印結(jié)果
/*
runningTotal = 0
結(jié)果 10
runningTotal = 10
結(jié)果 20
*/
//兩次調(diào)用incrementer閉包是同一個(gè) runningTotal 被保留下來瓦侮,所以閉包是引用類型的
- 逃逸閉包和非逃逸閉包
如果這個(gè)閉包是在這個(gè)函數(shù)結(jié)束前內(nèi)被調(diào)用,就是非逃逸的即noescape佣谐。
如果這個(gè)閉包是在函數(shù)執(zhí)行完后才被調(diào)用,調(diào)用的地方超過了這函數(shù)的范圍方妖,所以叫逃逸閉包狭魂。
例如:snapkit的添加約束的方法就是非逃逸的。因?yàn)檫@閉包馬上就執(zhí)行了
public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.makeConstraints(item: self.view, closure: closure)
}
網(wǎng)絡(luò)請求請求結(jié)束后的回調(diào)的閉包則是逃逸的党觅,因?yàn)榘l(fā)起請求后過了一段時(shí)間后這個(gè)閉包才執(zhí)行雌澄。比如這個(gè)Alamofire里的處理返回json的completionHandler閉包,就是逃逸的杯瞻。
public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
delegate.queue.addOperation {
(queue ?? DispatchQueue.main).async {
var dataResponse = DefaultDataResponse(
request: self.request,
response: self.response,
data: self.delegate.data,
error: self.delegate.error,
timeline: self.timeline
)
dataResponse.add(self.delegate.metrics)
completionHandler(dataResponse)
}
}
return self
}
舉個(gè)例子镐牺,寫一個(gè)模擬一個(gè)網(wǎng)絡(luò)請求,1秒才執(zhí)行魁莉,所以是逃逸閉包睬涧。
//此處編譯不通過
func loadRequest(callBack: () -> Void) -> Void {
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) {
callBack()
}
}
//下面是正確的寫法
func loadRequest(callBack: @escaping () -> Void) -> Void {
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) {
callBack()
}
}
這樣就需要顯示的聲明@escaping才能編譯通過。
AD743289-18E8-43BC-8131-9B0C5D4A50C6.png