Swift的闭包(⼆):捕获值
闭包可以从定义它的上下⽂中捕获常量和变量。
在Swift中,捕获值最简单的例⼦是嵌套函数,举个例⼦:
1 func makeIncrementer(forIncrement amount: Int) -> () -> Int {
2var runningTotal = 0
3 func incrementer() -> Int {
4 runningTotal += amount
5return runningTotal
6 }
7return incrementer
创业政策8 }
在这个例⼦中incrementer()捕获两个值,分别是amount、runningTotal。可以运⾏⼀下,观察结果:
1 let incrementByTen = makeIncrementer(forIncrement: 10)
2 print(incrementByTen()) //10
3 print(incrementByTen()) //20
4 let incrementByNine = makeIncrementer(forIncrement: 9)
5 print(incrementByNine()) //9
6 print(incrementByNine()) //18
7 print(incrementByTen()) //30
注意:如果你把闭包赋值给⼀个类实例的⼀个属性,并且闭包通过指向(refer fo)实例或者实例的成员捕获值,那么,在闭包和这个实例间就会有⼀个强引⽤环。
京东创始人
闭包是引⽤类型(Reference Types)
闭包和函数都是引⽤类型。
我的老师韩国
Nonescaping Closures
当⼀个闭包作为参数传递给⼀个函数,但是在函数返回后调⽤的时候,我们说⼀个闭包是escaped的。当你声明⼀个有⼀个闭包作为参数的函数的时候,你可以在参数类型前写@nonescape来暗⽰这个closure不允许escape。如:
1 func someFunctionWithNonescapingClosure(closure: @noescape () -> Void) {
2 closure()
3 }
把⼀个闭包标记⽤@nonescape让你在闭包内隐式的引⽤(refer to)lf,看下这个例⼦:
1class SomeClass {
2var x = 10
3 func doSomething() {
4 someFunctionWithNonescapingClosure { x = 200 }
5 someFunctionWithEscapingClosure { lf.x = 100 }
6 }
7 }
8
9 let instance = SomeClass()
10 instance.doSomething()
11 print(instance.x)
12// Prints "200"
13
14 completionHandlers.first?()
15 print(instance.x)
16// Prints "100"
Autoclosures
清塘荷韵An autoclosure is a closure that is automatically created to wrap an expression that's being pasd as an argument to a function. It doesn't take any arguments, and when it's called, it returns the value of the expression that's wrapped inside of it.
Autoclosures可以延迟计算(delay evaluation),因为直到调⽤闭包时,闭包内的代码才被运⾏。延迟计算对于有副作⽤或者计算代价昂贵的代码⾮常有⽤,因为你可以控制什么时候代码进⾏evaluation。
1var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
书法家王羲之2 unt)
说唱脸谱歌词3// Prints "5"
4
5 let customerProvider = { ve(at: 0) }
6 unt)
7// Prints "5"
8
9 print("Now rving \(customerProvider())!")
10// Prints "Now rving Chris!"
11 unt)
12// Prints "4"
也可以传递给⼀个参数:
1// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
2 func rve(customer customerProvider: () -> String) {
飞字行书
3 print("Now rving \(customerProvider())!")
4 }
5 rve(customer: { ve(at: 0) } )
6// Prints "Now rving Alex!"
使⽤@autoclosure:
1// customersInLine is ["Ewa", "Barry", "Daniella"]
2 func rve(customer customerProvider: @autoclosure () -> String) {
3 print("Now rving \(customerProvider())!")
4 }
5 rve(customer: ve(at: 0))
6// Prints "Now rving Ewa!"
注意:滥⽤autoclosure会使代码晦涩难懂。
@autoclosure属性隐含了@nonescape属性,如果你想要⼀个autoclosure允许esacpe,可以这样使⽤ @autoclosure(escaping) ,如: 1// customersInLine is ["Barry", "Daniella"]
形容结束的词语2var customerProviders: [() -> String] = []
3 func collectCustomerProviders(_ customerProvider: @autoclosure(escaping) () -> String) {
4 customerProviders.append(customerProvider)
5 }
6 ve(at: 0))
7 ve(at: 0))
8
9 print("Collected \(unt) closures.")
10// Prints "Collected 2 closures."
11for customerProvider in customerProviders {
12 print("Now rving \(customerProvider())!")
13 }
14// Prints "Now rving Barry!"
15// Prints "Now rving Daniella!"