我認爲混亂是由於太過努力的價值類型與參考類型。這與此無關。讓我們將數字作爲參考類型:
class RefInt: CustomStringConvertible {
let value: Int
init(value: Int) { self.value = value }
var description: String { return "\(value)" }
}
let counter:() -> RefInt
var count = RefInt(value: 0)
do {
counter = {
count = RefInt(value: count.value + 1)
return count
}
}
count = RefInt(value: count.value + 1) // 1
counter() // 2
counter() // 3
這種感覺有什麼不同嗎?我希望不是。這是同樣的事情,只是在參考。這不是一個價值/參考的東西。
問題是,正如你注意到的那樣,閉包捕獲變量。不是變量的值,或變量指向的引用的值,而是變量本身)。因此,在所有其他捕獲該變量的位置(包括調用者)中都會看到對閉包內變量的更改。在Capturing Values中對此進行了更全面的討論。如果你有興趣(我現在進入有點技術性,可能會超出你在乎什麼,現在的)
深一點:
瓶蓋居然有變量的引用,並改變它們會立即發生,包括調用didSet
等。這與inout
參數不同,該參數僅在返回時纔將值分配給其原始上下文。你可以看到,這種方式:
let counter:() -> Int
var count = 0 {
didSet { print("set count") }
}
do {
counter = {
count += 1
print("incremented count")
return count
}
}
func increaseCount(count: inout Int) {
count += 1
print("increased Count")
}
print("1")
count += 1 // 1
print("2")
counter() // 2
print("3")
counter() // 3
increaseCount(count: &count)
這版畫:「增加數量」
1
set count
2
set count
incremented count
3
set count
incremented count
increased Count
set count
注「設定計數」怎麼總是前「增加的計」,但後這使得閉包真正引用了它們捕獲的同一個變量(而不是值或引用;變量),以及爲什麼我們稱它爲捕獲閉包,而不是「傳遞」到函數。 (當然,您也可以「通過」關閉,在這種情況下,它們的行爲與這些參數的功能完全相同。)
我不確定你想獲得多少技術細節,但https://stackoverflow.com/a/40979548/2976878&https://stackoverflow.com/q/43171341/2976878可能是有用。基本上,捕獲的值被放入堆中的引用計數框中,然後存儲在函數值的上下文對象中。當涉及到捕獲具有引用類型的變量時,引用本身被放入堆分配框(考慮引用*本身*是值類型),所以您實際上是對引用的引用。 – Hamish
我看過你的文章,這很刺激。我瞭解內容。但是,什麼是參考計數盒?我讀了你多次展示的答案,但我沒有見過這個詞。你能解釋一下嗎? –
很高興你發現它很有用!一個盒子只是一個給定值的包裝,所以在閉包捕獲時,捕獲的變量的值在結構中堆積爲「盒子」,並在我的答案中包含專門的「盒子'結構的佈局。然後(主要)對該變量的任何未來訪問(在其定義的範圍內以及閉包中)僅被編譯爲訪問堆上的裝箱值。 –
Hamish