Swift捕獲值
捕獲值(Capturing Values)
閉包可以在其定義的上下文中捕獲常量或變量。 即使定義這些常量和變量的原域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
Swift最簡單的閉包形式是嵌套函數,也就是定義在其他函數的函數體內的函數。 嵌套函數可以捕獲其外部函數所有的參數以及定義的常量和變量。
下例爲一個叫做makeIncrementor
的函數,其包含了一個叫做incrementor
嵌套函數。 嵌套函數incrementor
從上下文中捕獲了兩個值,runningTotal
和amount
。 之後makeIncrementor
將incrementor
作爲閉包返回。 每次調用incrementor
時,其會以amount
作爲增量增加runningTotal
的值。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
makeIncrementor
返回類型爲() -> Int
。 這意味着其返回的是一個函數,而不是一個簡單類型值。 該函數在每次調用時不接受參數只返回一個Int
類型的值。 關於函數返回其他函數的內容,請查看函數類型作爲返回類型。
makeIncrementor
函數定義了一個整型變量runningTotal
(初始爲0) 用來存儲當前跑步總數。 該值通過incrementor
返回。
makeIncrementor
有一個Int
類型的參數,其外部命名爲forIncrement
, 內部命名爲amount
,表示每次incrementor
被調用時runningTotal
將要增加的量。
incrementor
函數用來執行實際的增加操作。 該函數簡單地使runningTotal
增加amount
,並將其返回。
如果我們單獨看這個函數,會發現看上去不同尋常:
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
incrementor
函數並沒有獲取任何參數,但是在函數體內訪問了runningTotal
和amount
變量。這是因爲其通過捕獲在包含它的函數體內已經存在的runningTotal
和amount
變量而實現。
由於沒有修改amount
變量,incrementor
實際上捕獲並存儲了該變量的一個副本,而該副本隨着incrementor
一同被存儲。
然而,因爲每次調用該函數的時候都會修改runningTotal
的值,incrementor
捕獲了當前runningTotal
變量的引用,而不是僅僅複製該變量的初始值。捕獲一個引用保證了當makeIncrementor
結束時候並不會消失,也保證了當下一次執行incrementor
函數時,runningTotal
可以繼續增加。
注意:
Swift 會決定捕獲引用還是拷貝值。
您不需要標註amount
或者runningTotal
來聲明在嵌入的incrementor
函數中的使用方式。
Swift 同時也處理runingTotal
變量的內存管理操作,如果不再被incrementor
函數使用,則會被清除。
下面代碼爲一個使用makeIncrementor
的例子:
let incrementByTen = makeIncrementor(forIncrement: 10)
該例子定義了一個叫做incrementByTen
的常量,該常量指向一個每次調用會加10的incrementor
函數。 調用這個函數多次可以得到以下結果:
incrementByTen()
// 返回的值爲10
incrementByTen()
// 返回的值爲20
incrementByTen()
// 返回的值爲30
如果您創建了另一個incrementor
,其會有一個屬於自己的獨立的runningTotal
變量的引用。 下面的例子中,incrementBySevne
捕獲了一個新的runningTotal
變量,該變量和incrementByTen
中捕獲的變量沒有任何聯繫:
let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
// 返回的值爲7
incrementByTen()
// 返回的值爲40
注意:
如果您將閉包賦值給一個類實例的屬性,並且該閉包通過指向該實例或其成員來捕獲了該實例,您將創建一個在閉包和實例間的強引用環。
Swift 使用捕獲列表來打破這種強引用環。更多信息,請參考 閉包引起的循環強引用。