閉包是特殊的函數(shù)
Swift 中,閉包其實(shí)是一個(gè)特殊的函數(shù),匿名函數(shù)
var block = {
print("3")
}
print(block)
輸出
(Function)
閉包語(yǔ)法
{ (參數(shù)) -> 返回值類型 in
閉包實(shí)現(xiàn)
return 返回值
}
簡(jiǎn)寫:
// 單行閉包 省略 return
let block1 = { (parm: Int) -> Int in parm + 1}
print(block1(3))
// 省略返回值類型 (單行多行閉包都可以)
let block2 = { (parm: Int) in parm + 1}
let block3 = { (parm: Int) in
return parm + 1
}
print(block3(2))
// 省略參數(shù)、返回值 in,直接用 $0 $1 來(lái)訪問(wèn)
let block4 = {
return $0 + 1
}
print(block4(5))
// 如果參數(shù)的類型已知(根據(jù)函數(shù)判斷),可以省略參數(shù)類型以及括號(hào)
func tmpFunc(block: (Int) -> (Void)) {
let a = 10
block(a)
}
tmpFunc { p0 in
print("\(p0 + 1)")
}
簡(jiǎn)寫規(guī)則:
如果是單行閉包,可以省略
return如果已知返回值類型(根據(jù)函數(shù)類型推斷),可以省略
返回值類型如果已知參數(shù)類型,可以省略
參數(shù)類型和參數(shù)的括號(hào)()如果用
$0 $1...來(lái)訪問(wèn)參數(shù),可以省略參數(shù)和int
簡(jiǎn)單來(lái)說(shuō),除了 大括號(hào){} 和 函數(shù)體 之外,滿足特定條件的話就都可以省略
尾隨閉包
尾隨閉包是特殊的閉包,即當(dāng)在函數(shù)的最后一個(gè)參數(shù)是閉包的時(shí)候,那么這個(gè)閉包是尾隨閉包
func theFunc(parm: Int, block: (Int) -> (Void)) {
}
上述函數(shù)的 block 是一個(gè)尾隨閉包
特性:
- 在調(diào)用尾隨閉包的時(shí)候,可以將閉包寫到參數(shù)括號(hào)外面:
theFunc(parm: 3) { (par) -> (Void) in
}
這樣可以增強(qiáng)可讀性
- 當(dāng)函數(shù)只有一個(gè)閉包作為參數(shù)的時(shí)候,調(diào)用的時(shí)候可以省略
括號(hào)()
func theFunc2(block: (Int) -> (Void)) {
}
theFunc2 { (par) -> (Void) in
}
逃逸閉包
閉包作為參數(shù)傳遞給函數(shù)的時(shí)候,其作用域?yàn)楹瘮?shù)內(nèi)部,即當(dāng)函數(shù)返回的時(shí)候,該閉包就釋放了,如果想在函數(shù)返回之后還能執(zhí)行,需要使用逃逸閉包。
如果不顯式指明閉包是逃逸閉包,則 默認(rèn)是非逃逸(non-escaping) 閉包。顯式指明為逃逸閉包需要在參數(shù)中增加 @escaping 關(guān)鍵字:
func func0(block: @escaping () -> ()) {
}
函數(shù) func0 接受的 block 是一個(gè)逃逸閉包,作用域不僅限于函數(shù)內(nèi)
通常用于需要保存閉包的場(chǎng)景:
var blocks:[() -> ()] = [() -> ()]()
func saveBlock(block: @escaping () -> ()) {
blocks.append(block)
}
// 保存 block
saveBlock {
print("hello")
}
// 在 saveBlock 方法返回后才執(zhí)行到這兒
let block = blocks[0]
block()
輸出:
hello
上述代碼和輸出表明,逃逸閉包的作用域不僅限于函數(shù)中。
自動(dòng)閉包
自動(dòng)閉包能將是普通閉包的一種,主要的區(qū)別的在聲明的時(shí)候不需要聲明參數(shù),在調(diào)用的時(shí)候傳入?yún)?shù),并且由 Swift 自動(dòng)將參數(shù)封裝成帶參數(shù)的閉包
這一種閉包需要在閉包前加上關(guān)鍵字 @autoclosure,如下:
func tmpFunc(isSuc: Bool, block: @autoclosure ()->(Bool)) {
if (isSuc) {
block()
}
}
該函數(shù)的 block 參數(shù)就是一個(gè)自動(dòng)閉包,在傳參的時(shí)候僅需要傳入一個(gè) 返回值為 Bool 的表達(dá)式即可,如下:
tmpFunc(isSuc: false, block: 2>3)
思考:為什么需要自動(dòng)閉包?(自動(dòng)閉包的使用場(chǎng)景)
主要是為了節(jié)省開銷,實(shí)際上,在 tmpFunc 的聲明時(shí)候,我們聲明其第二個(gè)參數(shù)是一個(gè) Bool 類型而無(wú)需是一個(gè)閉包也能很好地工作。但是假如后面的表達(dá)式需要耗費(fèi)大量的時(shí)間去計(jì)算的話,且第一個(gè)參數(shù)為 false,就會(huì)造成不必要的開銷,因?yàn)槲覀兏緹o(wú)需計(jì)算第二個(gè)參數(shù)的值。
可以看出使用閉包可以省去不必要的提前計(jì)算。
func tmpFunc(isSuc: Bool, block: @autoclosure ()->(Bool)) {
if (isSuc) {
if (block()) {
print("a")
}
}
}
func tmpFunc2(isSuc: Bool, block: Bool) {
if (isSuc) {
if (block) {
print("b")
}
}
}
tmpFunc(isSuc: false, block: 2>3 && 2>3 && 2>3 && 2>3 && 2>3 && 2>3)
tmpFunc2(isSuc: false, block: 2>3 && 2>3 && 2>3 && 2>3 && 2>3 && 2>3)
tmpFunc 僅在 isSuc 為 true 的時(shí)候會(huì)調(diào)用 2>3 && 2>3 && 2>3 && 2>3 && 2>3 && 2>3 這個(gè)復(fù)雜表達(dá)式,而 tmpFunc2 無(wú)論 isSuc 為何值都會(huì)調(diào)用那個(gè)復(fù)雜的表達(dá)式,造成了不必要的復(fù)雜計(jì)算。