结论:
- Swift closures capture a reference to the outer variables that you happen to use inside the closure. Swift闭包默认对外部变量捕获其引用
- That reference gets evaluated at the time the closure itself gets executed. 该引用在闭包执行的时候计算(读取值)
- Being a capture of the reference to the variable (and not the variable’s value itself), you can modify the variable’s value from within the closure (if that variable is declared as
var
and notlet
, of course) 捕获引用后可以在闭包内部修改变量的值,当然前提得是var而不是let的- You can instead tell Swift to evaluate a variable at the point of the closure creation and store that value in a local constant, instead of capturing the variable itself. You do that using capture lists expressed inside brackets. 可以通过捕获列表(capture list)来告诉Swift捕获变量的内容而不是变量本身(引用)
class Pokemon: CustomDebugStringConvertible {
let name: String
init(name: String) {
self.name = name
}
var debugDescription: String { return "<Pokemon \(name)>" }
deinit { print("\(self) deinit...!") }
}
func delay(_ seconds: Int, closure: @escaping ()->()) {
let time = DispatchTime.now() + .seconds(seconds)
DispatchQueue.main.asyncAfter(deadline: time) {
print("")
closure()
}
}
var pokemon = Pokemon(name: "Pikachu") //如果这里用var而非let的话,在闭包内部可以修改变量的值,但这并不影响下面Swift的捕获规则
print("before closure: \(pokemon)")
delay(1) {
print("inside closure: \(pokemon)")
//Swift captures variables by reference by default. Swift默认情况下通过引用捕获变量,且默认为strong强引用,目的是pokenmon在Closure被调用时还存在
//Swift并不是捕获“Pikachu”,而是pokemon变量,所以执行闭包Closure时得到的是“Mewtwo”
}
pokemon = Pokemon(name: "Mewtwo")
print("after closure: \(pokemon)")
/*
before closure: <Pokemon Pikachu>
<Pokemon Pikachu> deinit...!
after closure: <Pokemon Mewtwo>
inside closure: <Pokemon Mewtwo>
<Pokemon Mewtwo> deinit...!
*/
//this works for value types too, like Int. 这也同样适用于值类型,如Int。
var value = 2
print("before closure: \(value)")
delay(1) {
print("inside closure: \(value)")
//it captures a reference to the variable(still refers to the same variable), not the variable content itself. 捕获的是对变量的引用而非变量内容。
}
value = 3
print("after closure: \(value)")
/*
before closure: 2
after closure: 3
inside closure: 3
*/
In Swift, Array
, String
, and Dictionary
are all value types. They behave much like a simple int
value in C, acting as a unique instance of that data. see Value and Reference Types. 在Swift里,Array、String、Dictionary都是值类型,它们就像Int一样,作为唯一的数据实例。
//Capturing a variable as a constant copy(the content of the value variable at the time of creation.),you can use a capture list.通过捕获列表捕获变量内容而不是其引用,这种方法同样适用于引用类型(reference type),如上面的Pokemon。
var aInt = 1
var bInt = 5
print("init: aInt=\(aInt);bInt=\(bInt)")
delay(1) { [a = aInt, b = bInt] in
print("closure: a=\(a);b=\(b)")
}
aInt += 1
bInt += 1
print("modified: aInt=\(aInt);bInt=\(bInt)")
/*
init: aInt=1;bInt=5
modified: aInt=2;bInt=6
closure: a=1;b=5
*/
var aPoke = Pokemon(name: "A")
var bPoke = Pokemon(name: "B")
print("init: aPoke=\(aPoke);bPoke=\(bPoke)")
delay(1) { [a = aPoke, b = bPoke] in
print("closure: a=\(a);b=\(b)")
}
aPoke = Pokemon(name: "AA")
bPoke = Pokemon(name: "BB")
print("modified: aPoke=\(aPoke);bPoke=\(bPoke)")
/*
init: aPoke=<Pokemon A>;bPoke=<Pokemon B>
modified: aPoke=<Pokemon AA>;bPoke=<Pokemon BB>
<Pokemon BB> deinit...!
<Pokemon AA> deinit...!
closure: a=<Pokemon A>;b=<Pokemon B>
<Pokemon A> deinit...!
<Pokemon B> deinit...!
*/
//等同于↓:
let aPoke = Pokemon(name: "A")
let bPoke = Pokemon(name: "B")
print("init: aPoke=\(aPoke);bPoke=\(bPoke)")
delay(1) {
print("closure: a=\(aPoke);b=\(bPoke)")
}
参考资料: