闭包的特点
swift的iOS的app中,遍布着各种闭包,闭包中经常出现捕获列表,我们经常用[weak self]来防止内存泄露。我们都知道swift闭包捕获的是引用,但是swift闭包任何时候都是捕获变量的引用吗?并不是!
现在我们来看一个简单的例子:
var a = 0
var b = 0
let closure : () -> () = { print(a, b) }
执行闭包
closure() // 0 0
改变 a, b 的值
a = 3
b = 7
再次调用
closure() // 3 7
我们可以很直观的看到现在的a,b值都改变了。swift的闭包中捕获到的值是捕获的引用。所以一旦你改变了这些捕获变量的值,在闭包中就会反应出来。
引用类型带来的问题
事实上,很多时候,闭包的特性并不总是那么直观,如果不仔细,很容易带来一些奇怪的错误。比如,我们创建一个Array,然后数组的类型是闭包
var arrayClosure : [() -> ()] = []
然后我们执行下面的代码,将闭包添加到数组中
var i = 0
for _ in 1...3 {
arrayClosure.append { print(i) }
i += 1
}
我们尝试条用数组中的三个闭包来看看最后的打印值:
arrayClosure[0]()// 3
arrayClosure[1]()// 3
arrayClosure[2]()// 3
和我们预想的不太一样哈,闭包打印的值竟然都是最后一次的值。意味着三个闭包引用的都是最后一次的i的值。事实上我们想要的值或许是1,2,3。想象一下,如果三个闭包每次捕获的都是每次i的值的副本。那么是不是会打印出1,2,3的结果?但是闭包捕获的是引用呀,怎么才能获取copy副本呢。没错,就是捕获列表。
好了,现在我们再看另一个例子,看起来和开篇的第一个例子差不多,但是这里我们加了" [c, d] in " ,关键字 in 的左边就是我们的重点,这里表示的不是数组,而是捕获列表。一旦用了捕获列表,闭包捕获的将不是原始值的引用,而是在闭包内部诡异的生成了一个原始值的copy副本。
var c = 0
var d = 0
let anotherClosure : () -> () = { [c, d] in
print(c, d)
}
c = 3
d = 7
调用闭包,我们看看打印结果:
anotherClosure() // 0, 0
所以,显而易见,当你使用了捕获列表之后,你无论怎么在闭包外面操作改变原始的值。闭包并不关心。因为这个时候它已经不是捕获的引用了,而是最初原始值的copy副本。所以文中的第二个例子如果想得到1,2,3的结果。使用捕获列表就完全可以解决,perfect!
所以对于swift的闭包而言,并不总是捕获的引用。这篇先到这里,文笔不好,大家多多关照(*^__^*)