更新至Swift 3.0
基本语法
Closures 在 Swift 中的概念类似 C 和 Objective-C 中的 blocks 和其它语言中的 lambdas.
{ (参数) -> return type in
表达式
}
举例:排序以下字符串数组
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
普通方法调用
func backwards(s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards) // 系统提供的sort方法
// reversed 等于 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
将自定义 backwards 方法作为参数传入 sort(_:) 方法用于排序
闭包调用
reversed = names.sort({ (s1: String, s2: String) -> Bool in
return s1 > s2
})
- 闭包写法和普通方法的区别
- 可以去掉 func 声明 和方法名, 将之后内容用 { } 包起来
- 由于参数后的 { 通过上步骤挪到前面去了, in 这个关键字可表示闭包内容的开始
- 其它照旧,这么短的方法可写成一行,简洁
根据 names 的 string 内容,和 sort 方法声明需要的参数
系统可自动推导出这个闭包的参数类型,所以可不写参数类型
reversed = names.sort( { s1, s2 in return s1 > s2 } )
闭包会把单行表达式的结果隐式 return 所以 return 也可以不用写
reversed = names.sort( { s1, s2 in s1 > s2 } )
Swift 提供了参数名的缩写,按顺序分配分别为 $0,$1,$2 以此类推
reversed = names.sort( { $0 > $1 } )
Swift 做了一些工作让代码可以使用 Operator Functions 让代码变的更简单
reversed = names.sort( { > } )
Trailing Closures | 尾随闭包
如果闭包作为参数传递给方法时,在最后一位,它允许被写在 ( ) 外面
reversed = names.sort() { $0 > $1 }
要是这方法只有这么一个闭包参数,那连括号都可以省掉了
reversed = names.sort { $0 > $1 }
其存在的意义是让代码更简洁,可读性更好
Capturing Values | 捕获数值
一个闭包可以获取在它周围上下文定义的常量或变量,哪怕这参数不存在了也依然可以
闭包属于引用类型,把闭包返回给一个对象时,如果闭包内有通过引用该对象,去方法其属性变量之类的操作,就会产生相互引用。从而导致的循环引用,会使内存无法释放
Nonescaping Closures | 非逃逸闭包
当一个闭包被当做参数传递给一个方法时,而且它会在这个方法返回后被调用,那它就算是个逃逸闭包
对于非逃逸的闭包,可以在闭包参数前声明关键字 @noescape 来告诉编译器,于是编译器就会做更多更激进的优化
func someFunctionWithNonescapingClosure(closure: @noescape () -> Void) {
closure()
}
Autoclosures | 自动闭包
当我们把一个闭包作为参数传递的时候,这个闭包有一对醒目的花括号来表示它是个闭包。哪怕它只有一行表达式
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// Prints "Now serving Alex!”
自动动闭包会把传递给函数的表达式包自动装成闭包,这样就不用每次都写一对 {} 了,只要在参数前声明 @autoclosure 关键字即可,但是这个闭包不接受任何参数,只计算闭包内的内容
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// Prints "Now serving Ewa!”
自动闭包隐式的就是个非逃逸闭包,如果要传递的是逃逸闭包,需要加上声明 @autoclosure(escaping) 即可
参考
- The Swift Programming Language (Swift 3.0) | Closures
- Autoclosures