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 使用捕獲列表來打破這種強引用環。更多信息,請參考 閉包引起的循環強引用。