Swift傻傻分不清楚系列(九)闭包
本頁包含內(nèi)容:
- 閉包表達(dá)式(Closure Expressions)
- 尾隨閉包(Trailing Closures)
- 值捕獲(Capturing Values)
- 閉包是引用類型(Closures Are Reference Types)
- 非逃逸閉包(Nonescaping Closures)
- 自動閉包(Autoclosures)
閉包是自包含的函數(shù)代碼塊,可以在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其他一些編程語言中的匿名函數(shù)比較相似。
閉包可以捕獲和存儲其所在上下文中任意常量和變量的引用。這就是所謂的閉合并包裹著這些常量和變量,俗稱閉包。Swift 會為您管理在捕獲過程中涉及到的所有內(nèi)存操作。
在函數(shù)章節(jié)中介紹的全局和嵌套函數(shù)實際上也是特殊的閉包,閉包采取如下三種形式之一:
- 全局函數(shù)是一個有名字但不會捕獲任何值的閉包
- 嵌套函數(shù)是一個有名字并可以捕獲其封閉函數(shù)域內(nèi)值的閉包
- 閉包表達(dá)式是一個利用輕量級語法所寫的可以捕獲其上下文中變量或常量值的匿名閉包
Swift 的閉包表達(dá)式擁有簡潔的風(fēng)格,并鼓勵在常見場景中進(jìn)行語法優(yōu)化,主要優(yōu)化如下:
- 利用上下文推斷參數(shù)和返回值類型
- 隱式返回單表達(dá)式閉包,即單表達(dá)式閉包可以省略return關(guān)鍵字
- 參數(shù)名稱縮寫
- 尾隨(Trailing)閉包語法
閉包表達(dá)式(Closure Expressions)
嵌套函數(shù)是一個在較復(fù)雜函數(shù)中方便進(jìn)行命名和定義自包含代碼模塊的方式。當(dāng)然,有時候撰寫小巧的沒有完整定義和命名的類函數(shù)結(jié)構(gòu)也是很有用處的,尤其是在您處理一些函數(shù)并需要將另外一些函數(shù)作為該函數(shù)的參數(shù)時。
閉包表達(dá)式是一種利用簡潔語法構(gòu)建內(nèi)聯(lián)閉包的方式。閉包表達(dá)式提供了一些語法優(yōu)化,使得撰寫閉包變得簡單明了。下面閉包表達(dá)式的例子通過使用幾次迭代展示了sort(_:)方法定義和語法優(yōu)化的方式。每一次迭代都用更簡潔的方式描述了相同的功能。
sort 方法(The Sort Method)
Swift 標(biāo)準(zhǔn)庫提供了名為sort的方法,會根據(jù)您提供的用于排序的閉包函數(shù)將已知類型數(shù)組中的值進(jìn)行排序。一旦排序完成,sort(_:)方法會返回一個與原數(shù)組大小相同,包含同類型元素且元素已正確排序的新數(shù)組。原數(shù)組不會被sort(_:)方法修改。
下面的閉包表達(dá)式示例使用sort(_:)方法對一個String類型的數(shù)組進(jìn)行字母逆序排序.以下是初始數(shù)組值:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]sort(_:)方法接受一個閉包,該閉包函數(shù)需要傳入與數(shù)組元素類型相同的兩個值,并返回一個布爾類型值來表明當(dāng)排序結(jié)束后傳入的第一個參數(shù)排在第二個參數(shù)前面還是后面。如果第一個參數(shù)值出現(xiàn)在第二個參數(shù)值前面,排序閉包函數(shù)需要返回true,反之返回false。
該例子對一個String類型的數(shù)組進(jìn)行排序,因此排序閉包函數(shù)類型需為(String, String) -> Bool。
提供排序閉包函數(shù)的一種方式是撰寫一個符合其類型要求的普通函數(shù),并將其作為sort(_:)方法的參數(shù)傳入:
func backwards(s1: String, s2: String) -> Bool {return s1 > s2 } var reversed = names.sort(backwards) // reversed 為 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]如果第一個字符串(s1)大于第二個字符串(s2),backwards(_:_:)函數(shù)返回true,表示在新的數(shù)組中s1應(yīng)該出現(xiàn)在s2前。對于字符串中的字符來說,“大于”表示“按照字母順序較晚出現(xiàn)”。這意味著字母"B"大于字母"A",字符串"Tom"大于字符串"Tim"。該閉包將進(jìn)行字母逆序排序,"Barry"將會排在"Alex"之前。
然而,這是一個相當(dāng)冗長的方式,本質(zhì)上只是寫了一個單表達(dá)式函數(shù) (a > b)。在下面的例子中,利用閉包表達(dá)式語法可以更好地構(gòu)造一個內(nèi)聯(lián)排序閉包。
閉包表達(dá)式語法(Closure Expression Syntax)
閉包表達(dá)式語法有如下一般形式:
{ (parameters) -> returnType instatements }閉包表達(dá)式語法可以使用常量、變量和inout類型作為參數(shù),不能提供默認(rèn)值。也可以在參數(shù)列表的最后使用可變參數(shù)。元組也可以作為參數(shù)和返回值。
下面的例子展示了之前backwards(_:_:)函數(shù)對應(yīng)的閉包表達(dá)式版本的代碼:
reversed = names.sort({ (s1: String, s2: String) -> Bool inreturn s1 > s2 })需要注意的是內(nèi)聯(lián)閉包參數(shù)和返回值類型聲明與backwards(_:_:)函數(shù)類型聲明相同。在這兩種方式中,都寫成了(s1: String, s2: String) -> Bool。然而在內(nèi)聯(lián)閉包表達(dá)式中,函數(shù)和返回值類型都寫在大括號內(nèi),而不是大括號外。
閉包的函數(shù)體部分由關(guān)鍵字in引入。該關(guān)鍵字表示閉包的參數(shù)和返回值類型定義已經(jīng)完成,閉包函數(shù)體即將開始。
由于這個閉包的函數(shù)體部分如此短,以至于可以將其改寫成一行代碼:
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )該例中sort(_:)方法的整體調(diào)用保持不變,一對圓括號仍然包裹住了方法的整個參數(shù)。然而,參數(shù)現(xiàn)在變成了內(nèi)聯(lián)閉包。
根據(jù)上下文推斷類型(Inferring Type From Context)
因為排序閉包函數(shù)是作為sort(_:)方法的參數(shù)傳入的,Swift 可以推斷其參數(shù)和返回值的類型。sort(_:)方法被一個字符串?dāng)?shù)組調(diào)用,因此其參數(shù)必須是(String, String) -> Bool類型的函數(shù)。這意味著(String, String)和Bool類型并不需要作為閉包表達(dá)式定義的一部分。因為所有的類型都可以被正確推斷,返回箭頭(->)和圍繞在參數(shù)周圍的括號也可以被省略:
reversed = names.sort( { s1, s2 in return s1 > s2 } )實際上任何情況下,通過內(nèi)聯(lián)閉包表達(dá)式構(gòu)造的閉包作為參數(shù)傳遞給函數(shù)或方法時,都可以推斷出閉包的參數(shù)和返回值類型。 這意味著閉包作為函數(shù)或者方法的參數(shù)時,您幾乎不需要利用完整格式構(gòu)造內(nèi)聯(lián)閉包。
盡管如此,您仍然可以明確寫出有著完整格式的閉包。如果完整格式的閉包能夠提高代碼的可讀性,則可以采用完整格式的閉包。而在sort(_:)方法這個例子里,閉包的目的就是排序。由于這個閉包是為了處理字符串?dāng)?shù)組的排序,因此讀者能夠推測出這個閉包是用于字符串處理的。
單表達(dá)式閉包隱式返回(Implicit Return From Single-Expression Clossures)
單行表達(dá)式閉包可以通過省略return關(guān)鍵字來隱式返回單行表達(dá)式的結(jié)果,如上版本的例子可以改寫為:
reversed = names.sort( { s1, s2 in s1 > s2 } )在這個例子中,sort(_:)方法的參數(shù)類型明確了閉包必須返回一個Bool類型值。因為閉包函數(shù)體只包含了一個單一表達(dá)式(s1 > s2),該表達(dá)式返回Bool類型值,因此這里沒有歧義,return關(guān)鍵字可以省略。
參數(shù)名稱縮寫(Shorthand Argument Names)
Swift 自動為內(nèi)聯(lián)閉包提供了參數(shù)名稱縮寫功能,您可以直接通過$0,$1,$2來順序調(diào)用閉包的參數(shù),以此類推。
如果您在閉包表達(dá)式中使用參數(shù)名稱縮寫,您可以在閉包參數(shù)列表中省略對其的定義,并且對應(yīng)參數(shù)名稱縮寫的類型會通過函數(shù)類型進(jìn)行推斷。in關(guān)鍵字也同樣可以被省略,因為此時閉包表達(dá)式完全由閉包函數(shù)體構(gòu)成:
reversed = names.sort( { $0 > $1 } )在這個例子中,$0和$1表示閉包中第一個和第二個String類型的參數(shù)。
運(yùn)算符函數(shù)(Operator Functions)
實際上還有一種更簡短的方式來撰寫上面例子中的閉包表達(dá)式。Swift 的String類型定義了關(guān)于大于號(>)的字符串實現(xiàn),其作為一個函數(shù)接受兩個String類型的參數(shù)并返回Bool類型的值。而這正好與sort(_:)方法的參數(shù)需要的函數(shù)類型相符合。因此,您可以簡單地傳遞一個大于號,Swift 可以自動推斷出您想使用大于號的字符串函數(shù)實現(xiàn):
reversed = names.sort(>)尾隨閉包(Trailing Closures)
如果您需要將一個很長的閉包表達(dá)式作為最后一個參數(shù)傳遞給函數(shù),可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性。尾隨閉包是一個書寫在函數(shù)括號之后的閉包表達(dá)式,函數(shù)支持將其作為最后一個參數(shù)調(diào)用:
func someFunctionThatTakesAClosure(closure: () -> Void) {// 函數(shù)體部分 }// 以下是不使用尾隨閉包進(jìn)行函數(shù)調(diào)用 someFunctionThatTakesAClosure({// 閉包主體部分 })// 以下是使用尾隨閉包進(jìn)行函數(shù)調(diào)用 someFunctionThatTakesAClosure() {// 閉包主體部分 }在閉包表達(dá)式語法一節(jié)中作為sort(_:)方法參數(shù)的字符串排序閉包可以改寫為:
reversed = names.sort() { $0 > $1 }如果函數(shù)只需要閉包表達(dá)式一個參數(shù),當(dāng)您使用尾隨閉包時,您甚至可以把()省略掉:
reversed = names.sort { $0 > $1 }當(dāng)閉包非常長以至于不能在一行中進(jìn)行書寫時,尾隨閉包變得非常有用。舉例來說,Swift 的Array類型有一個map(_:)方法,其獲取一個閉包表達(dá)式作為其唯一參數(shù)。該閉包函數(shù)會為數(shù)組中的每一個元素調(diào)用一次,并返回該元素所映射的值。具體的映射方式和返回值類型由閉包來指定。
當(dāng)提供給數(shù)組的閉包應(yīng)用于每個數(shù)組元素后,map(_:)方法將返回一個新的數(shù)組,數(shù)組中包含了與原數(shù)組中的元素一一對應(yīng)的映射后的值。
下例介紹了如何在map(_:)方法中使用尾隨閉包將Int類型數(shù)組[16, 58, 510]轉(zhuǎn)換為包含對應(yīng)String類型的值的數(shù)組["OneSix", "FiveEight", "FiveOneZero"]:
let digitNames = [0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510]如上代碼創(chuàng)建了一個數(shù)字位和它們英文版本名字相映射的字典。同時還定義了一個準(zhǔn)備轉(zhuǎn)換為字符串?dāng)?shù)組的整型數(shù)組。
您現(xiàn)在可以通過傳遞一個尾隨閉包給numbers的map(_:)方法來創(chuàng)建對應(yīng)的字符串版本數(shù)組:
let strings = numbers.map {(number) -> String invar number = numbervar output = ""while number > 0 {output = digitNames[number % 10]! + outputnumber /= 10}return output } // strings 常量被推斷為字符串類型數(shù)組,即 [String] // 其值為 ["OneSix", "FiveEight", "FiveOneZero"]map(_:)為數(shù)組中每一個元素調(diào)用了閉包表達(dá)式。您不需要指定閉包的輸入?yún)?shù)number的類型,因為可以通過要映射的數(shù)組類型進(jìn)行推斷。
在該例中,局部變量number的值由閉包中的number參數(shù)獲得,因此可以在閉包函數(shù)體內(nèi)對其進(jìn)行修改,(閉包或者函數(shù)的參數(shù)總是常量),閉包表達(dá)式指定了返回類型為String,以表明存儲映射值的新數(shù)組類型為String。
閉包表達(dá)式在每次被調(diào)用的時候創(chuàng)建了一個叫做output的字符串并返回。其使用求余運(yùn)算符(number % 10)計算最后一位數(shù)字并利用digitNames字典獲取所映射的字符串。
注意
字典digitNames下標(biāo)后跟著一個嘆號(!),因為字典下標(biāo)返回一個可選值(optional value),表明該鍵不存在時會查找失敗。在上例中,由于可以確定number % 10總是digitNames字典的有效下標(biāo),因此嘆號可以用于強(qiáng)制解包 (force-unwrap) 存儲在下標(biāo)的可選類型的返回值中的String類型的值。
從digitNames字典中獲取的字符串被添加到output的前部,逆序建立了一個字符串版本的數(shù)字。(在表達(dá)式number % 10中,如果number為16,則返回6,58返回8,510返回0。)
number變量之后除以10。因為其是整數(shù),在計算過程中未除盡部分被忽略。因此16變成了1,58變成了5,510變成了51。
整個過程重復(fù)進(jìn)行,直到number /= 10為0,這時閉包會將字符串output返回,而map(_:)方法則會將字符串添加到所映射的數(shù)組中。
在上面的例子中,通過尾隨閉包語法,優(yōu)雅地在函數(shù)后封裝了閉包的具體功能,而不再需要將整個閉包包裹在map(_:)方法的括號內(nèi)。
捕獲值(Capturing Values)
閉包可以在其被定義的上下文中捕獲常量或變量。即使定義這些常量和變量的原作用域已經(jīng)不存在,閉包仍然可以在閉包函數(shù)體內(nèi)引用和修改這些值。
Swift 中,可以捕獲值的閉包的最簡單形式是嵌套函數(shù),也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù)。嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量。
舉個例子,這有一個叫做makeIncrementor的函數(shù),其包含了一個叫做incrementor的嵌套函數(shù)。嵌套函數(shù)incrementor()從上下文中捕獲了兩個值,runningTotal和amount。捕獲這些值之后,makeIncrementor將incrementor作為閉包返回。每次調(diào)用incrementor時,其會以amount作為增量增加runningTotal的值。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {var runningTotal = 0func incrementor() -> Int {runningTotal += amountreturn runningTotal}return incrementor }makeIncrementor返回類型為() -> Int。這意味著其返回的是一個函數(shù),而不是一個簡單類型的值。該函數(shù)在每次調(diào)用時不接受參數(shù),只返回一個Int類型的值。
makeIncrementer(forIncrement:)函數(shù)定義了一個初始值為0的整型變量runningTotal,用來存儲當(dāng)前跑步總數(shù)。該值通過incrementor返回。
makeIncrementer(forIncrement:)有一個Int類型的參數(shù),其外部參數(shù)名為forIncrement,內(nèi)部參數(shù)名為amount,該參數(shù)表示每次incrementor被調(diào)用時runningTotal將要增加的量。
嵌套函數(shù)incrementor用來執(zhí)行實際的增加操作。該函數(shù)簡單地使runningTotal增加amount,并將其返回。
如果我們單獨看這個函數(shù),會發(fā)現(xiàn)看上去不同尋常:
func incrementor() -> Int {runningTotal += amountreturn runningTotal }incrementer()函數(shù)并沒有任何參數(shù),但是在函數(shù)體內(nèi)訪問了runningTotal和amount變量。這是因為它從外圍函數(shù)捕獲了runningTotal和amount變量的引用。捕獲引用保證了runningTotal和amount變量在調(diào)用完makeIncrementer后不會消失,并且保證了在下一次執(zhí)行incrementer函數(shù)時,runningTotal依舊存在。
注意
為了優(yōu)化,如果一個值是不可變的,Swift 可能會改為捕獲并保存一份對值的拷貝。
Swift 也會負(fù)責(zé)被捕獲變量的所有內(nèi)存管理工作,包括釋放不再需要的變量。
下面是一個使用makeIncrementor的例子:
let incrementByTen = makeIncrementor(forIncrement: 10)該例子定義了一個叫做incrementByTen的常量,該常量指向一個每次調(diào)用會將runningTotal變量增加10的incrementor函數(shù)。調(diào)用這個函數(shù)多次可以得到以下結(jié)果:
incrementByTen() // 返回的值為10 incrementByTen() // 返回的值為20 incrementByTen() // 返回的值為30如果您創(chuàng)建了另一個incrementor,它會有屬于它自己的一個全新、獨立的runningTotal變量的引用:
let incrementBySeven = makeIncrementor(forIncrement: 7) incrementBySeven() // 返回的值為7再次調(diào)用原來的incrementByTen會在原來的變量runningTotal上繼續(xù)增加值,該變量和incrementBySeven中捕獲的變量沒有任何聯(lián)系:
incrementByTen() // 返回的值為40注意
如果您將閉包賦值給一個類實例的屬性,并且該閉包通過訪問該實例或其成員而捕獲了該實例,您將創(chuàng)建一個在閉包和該實例間的循環(huán)強(qiáng)引用。Swift 使用捕獲列表來打破這種循環(huán)強(qiáng)引用。
閉包是引用類型(Closures Are Reference Types)
上面的例子中,incrementBySeven和incrementByTen是常量,但是這些常量指向的閉包仍然可以增加其捕獲的變量的值。這是因為函數(shù)和閉包都是引用類型。
無論您將函數(shù)或閉包賦值給一個常量還是變量,您實際上都是將常量或變量的值設(shè)置為對應(yīng)函數(shù)或閉包的引用。上面的例子中,指向閉包的引用incrementByTen是一個常量,而并非閉包內(nèi)容本身。
這也意味著如果您將閉包賦值給了兩個不同的常量或變量,兩個值都會指向同一個閉包:
let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // 返回的值為50非逃逸閉包(Nonescaping Closures)
當(dāng)一個閉包作為參數(shù)傳到一個函數(shù)中,但是這個閉包在函數(shù)返回之后才被執(zhí)行,我們稱該閉包從函數(shù)中逃逸。當(dāng)你定義接受閉包作為參數(shù)的函數(shù)時,你可以在參數(shù)名之前標(biāo)注@noescape,用來指明這個閉包是不允許“逃逸”出這個函數(shù)的。將閉包標(biāo)注@noescape能使編譯器知道這個閉包的生命周期(譯者注:閉包只能在函數(shù)體中被執(zhí)行,不能脫離函數(shù)體執(zhí)行,所以編譯器明確知道運(yùn)行時的上下文),從而可以進(jìn)行一些比較激進(jìn)的優(yōu)化。
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {closure() }舉個例子,sort(_:)方法接受一個用來進(jìn)行元素比較的閉包作為參數(shù)。這個參數(shù)被標(biāo)注了@noescape,因為它確保自己在排序結(jié)束之后就沒用了。
一種能使閉包“逃逸”出函數(shù)的方法是,將這個閉包保存在一個函數(shù)外部定義的變量中。舉個例子,很多啟動異步操作的函數(shù)接受一個閉包參數(shù)作為 completion handler。這類函數(shù)會在異步操作開始之后立刻返回,但是閉包直到異步操作結(jié)束后才會被調(diào)用。在這種情況下,閉包需要“逃逸”出函數(shù),因為閉包需要在函數(shù)返回之后被調(diào)用。例如:
var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: () -> Void) {completionHandlers.append(completionHandler) }someFunctionWithEscapingClosure(_:)函數(shù)接受一個閉包作為參數(shù),該閉包被添加到一個函數(shù)外定義的數(shù)組中。如果你試圖將這個參數(shù)標(biāo)注為@noescape,你將會獲得一個編譯錯誤。
將閉包標(biāo)注為@noescape使你能在閉包中隱式地引用self。
class SomeClass {var x = 10func doSomething() {someFunctionWithEscapingClosure { self.x = 100 }someFunctionWithNoescapeClosure { x = 200 }} }let instance = SomeClass() instance.doSomething() print(instance.x) // prints "200"completionHandlers.first?() print(instance.x) // prints "100"自動閉包(Autoclosures)
自動閉包是一種自動創(chuàng)建的閉包,用于包裝傳遞給函數(shù)作為參數(shù)的表達(dá)式。這種閉包不接受任何參數(shù),當(dāng)它被調(diào)用的時候,會返回被包裝在其中的表達(dá)式的值。這種便利語法讓你能夠用一個普通的表達(dá)式來代替顯式的閉包,從而省略閉包的花括號。
我們經(jīng)常會調(diào)用一個接受閉包作為參數(shù)的函數(shù),但是很少實現(xiàn)那樣的函數(shù)。舉個例子來說,assert(condition:message:file:line:)函數(shù)接受閉包作為它的condition參數(shù)和message參數(shù);它的condition參數(shù)僅會在 debug 模式下被求值,它的message參數(shù)僅當(dāng)condition參數(shù)為false時被計算求值。
自動閉包讓你能夠延遲求值,因為代碼段不會被執(zhí)行直到你調(diào)用這個閉包。延遲求值對于那些有副作用(Side Effect)和代價昂貴的代碼來說是很有益處的,因為你能控制代碼什么時候執(zhí)行。下面的代碼展示了閉包如何延時求值。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] print(customersInLine.count) // prints "5"let customerProvider = { customersInLine.removeAtIndex(0) } print(customersInLine.count) // prints "5"print("Now serving \(customerProvider())!") // prints "Now serving Chris!" print(customersInLine.count) // prints "4"盡管在閉包的代碼中,customersInLine的第一個元素被移除了,不過在閉包被調(diào)用之前,這個元素是不會被移除的。如果這個閉包永遠(yuǎn)不被調(diào)用,那么在閉包里面的表達(dá)式將永遠(yuǎn)不會執(zhí)行,那意味著列表中的元素永遠(yuǎn)不會被移除。請注意,customerProvider的類型不是String,而是() -> String,一個沒有參數(shù)且返回值為String的函數(shù)。
將閉包作為參數(shù)傳遞給函數(shù)時,你能獲得同樣的延時求值行為。
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"] func serveCustomer(customerProvider: () -> String) {print("Now serving \(customerProvider())!") } serveCustomer( { customersInLine.removeAtIndex(0) } ) // prints "Now serving Alex!"serveCustomer(_:)接受一個返回顧客名字的顯式的閉包。下面這個版本的serveCustomer(_:)完成了相同的操作,不過它并沒有接受一個顯式的閉包,而是通過將參數(shù)標(biāo)記為@autoclosure來接收一個自動閉包?,F(xiàn)在你可以將該函數(shù)當(dāng)做接受String類型參數(shù)的函數(shù)來調(diào)用。customerProvider參數(shù)將自動轉(zhuǎn)化為一個閉包,因為該參數(shù)被標(biāo)記了@autoclosure特性。
// customersInLine is ["Ewa", "Barry", "Daniella"] func serveCustomer(@autoclosure customerProvider: () -> String) {print("Now serving \(customerProvider())!") } serveCustomer(customersInLine.removeAtIndex(0)) // prints "Now serving Ewa!"注意
過度使用autoclosures會讓你的代碼變得難以理解。上下文和函數(shù)名應(yīng)該能夠清晰地表明求值是被延遲執(zhí)行的。
@autoclosure特性暗含了@noescape特性,這個特性在非逃逸閉包一節(jié)中有描述。如果你想讓這個閉包可以“逃逸”,則應(yīng)該使用@autoclosure(escaping)特性.
// customersInLine is ["Barry", "Daniella"] var customerProviders: [() -> String] = [] func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {customerProviders.append(customerProvider) } collectCustomerProviders(customersInLine.removeAtIndex(0)) collectCustomerProviders(customersInLine.removeAtIndex(0))print("Collected \(customerProviders.count) closures.") // prints "Collected 2 closures." for customerProvider in customerProviders {print("Now serving \(customerProvider())!") } // prints "Now serving Barry!" // prints "Now serving Daniella!"在上面的代碼中,collectCustomerProviders(_:)函數(shù)并沒有調(diào)用傳入的customerProvider閉包,而是將閉包追加到了customerProviders數(shù)組中。這個數(shù)組定義在函數(shù)作用域范圍外,這意味著數(shù)組內(nèi)的閉包將會在函數(shù)返回之后被調(diào)用。因此,customerProvider參數(shù)必須允許“逃逸”出函數(shù)作用域。
再次感謝~~~~~~~~~~~~~
總結(jié)
以上是生活随笔為你收集整理的Swift傻傻分不清楚系列(九)闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Swift傻傻分不清楚系列(八)函数
- 下一篇: 检测字符串包含emoji表情