閉包介紹
Swift閉包和Objective-C中的Block很類似,是一段自包含的函數代碼塊,可以在代碼中使用和傳遞。相當于一個匿名函數。
函數:是一個有名字的閉包。
普通函數:是一個有名字但不會捕獲任何值的閉包。
嵌套函數:是一個有名字并可以捕獲其封閉函數域內值的閉包。
閉包格式
閉包表達式語法有如下的一般形式:
{ (parameters) -> returnType in
statements
}
閉包使用、尾隨閉包、閉包簡寫格式
// 以下閉包以Array的sorted函數為例
func testClosure() {
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ str1: String, _ str2: String) -> Bool {
return str1 > str2
}
// sorted函數的參數是一個閉包,下面傳了一個方法名,由此說明:嵌套函數是一個有名字但并可以捕獲其封閉函數域內值的閉包
var reversedNames = names.sorted(by: backward)
// 普通閉包格式:(參數: 參數類型,...) -> 返回值類型 in ...
reversedNames = names.sorted(by: {(_ str1: String, _ str2: String) -> Bool in return str1 > str2})
// 根據Swift的類型推斷,參數類型及參數括號可以去掉,返回值類型可以去掉
reversedNames = names.sorted(by: {str1, str2 in return str1 > str2})
// 單行表達式:可以去掉return
reversedNames = names.sorted(by: {str1, str2 in str1 > str2})
// 使用參數名縮寫:參數和in也可以去掉
reversedNames = names.sorted(by: {$0 > $1})
// 使用運算符:因為Swift中為字符串重載了大于號小于號
reversedNames = names.sorted(by: >)
// 使用尾隨閉包:前提是閉包必須是函數的最后一個參數
reversedNames = names.sorted() {$0 < $1}
// 使用尾隨閉包:閉包是函數唯一參數時,可以省掉參數括號
reversedNames = names.sorted {$0 < $1}
print(reversedNames)
}
閉包值捕獲,閉包是引用類型
先貼代碼:
// 值捕獲示例:下面嵌套函數incrementer本身沒有參數,它卻捕獲了外圍的參數runningTotal和amount
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
// 調用
let makeIncrementByTen = makeIncrementer(forIncrement: 10)
var ret = makeIncrementByTen()
print("ret= \(ret)")// ret= 10
ret = makeIncrementByTen()
print("ret= \(ret)")// ret= 20
ret = makeIncrementByTen()
print("ret= \(ret)")// ret= 30
let makeIncrementBySeven = makeIncrementer(forIncrement: 7)
ret = makeIncrementBySeven()
print("ret= \(ret)")// ret= 7
ret = makeIncrementByTen()
print("ret= \(ret)")// ret= 40
// 上面的同一個閉包連續調用,值會遞增原因:閉包是引用類型,同一個閉包捕獲的變量并沒有釋放
逃逸閉包
逃逸閉包概念:當一個閉包作為參數傳到一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸。逃逸閉包多用來做函數回調,與Objective-C里的Block有異曲同工之妙。
代碼示例:
// 逃逸閉包示例:下面的閉包被方法外的數組引用,也就意味著,這個閉包在函數執行完后還可能被調用,所以必須使用逃逸閉包,不然編譯不過去
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
// 非逃逸閉包
func someFunctionWithNoneEscapingClosure(closure: () -> Void) {
closure()
}
var x = 10
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 逃逸閉包
someFunctionWithEscapingClosure {
// 注意:逃逸閉包類必須顯式的引用self,
self.x = 100
}
someFunctionWithNoneEscapingClosure {
x = 200
}
print("x= \(x)")// x= 200
completionHandlers.first?()
print("x= \(x)")// x= 100
}
自動閉包
自動閉包:閉包作為參數傳遞給函數時,可以將閉包定義為自動閉包(使用關鍵字@autoclosure)。這樣傳遞參數時,可以直接傳遞一段代碼(或者一個變量、表達式),系統會自動將這段代碼轉化成閉包。需要注意:過度使用 autoclosures 會讓你的代碼變得難以理解。這里不貼代碼了。