位置:首頁 > 高級語言 > Swift教學 > Swift捕獲值

Swift捕獲值

捕獲值(Capturing Values)

閉包可以在其定義的上下文中捕獲常量或變量。 即使定義這些常量和變量的原域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。

Swift最簡單的閉包形式是嵌套函數,也就是定義在其他函數的函數體內的函數。 嵌套函數可以捕獲其外部函數所有的參數以及定義的常量和變量。

下例為一個叫做makeIncrementor的函數,其包含了一個叫做incrementor嵌套函數。 嵌套函數incrementor從上下文中捕獲了兩個值,runningTotalamount。 之後makeIncrementorincrementor作為閉包返回。 每次調用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函數並冇有獲取任何參數,但是在函數體內訪問了runningTotalamount變量。這是因為其通過捕獲在包含它的函數體內已經存在的runningTotalamount變量而實現。

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