闭包是自包含的函数代码块,可以在代码中被传递和使用。我的理解就是一段代码可以直接使用和传递,不需要想函数一样被定义和调用,但是有函数的功能,也有参数,返回值这些信息,比函数更加简洁。Java 8中也新增了闭包,但是在Java 8以下是没有闭包的,要想实现同样的功能需要接口。
下面以sort为例来介绍Java和Swift不使用闭包来实现排序的方法,先看Java的:
public static void main(String[] args) {
String[] names = {"Chris", "Alex", "Ewa", "Barry", "Daniella"};
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return -(s1.compareTo(s2));
}
});
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
}
我们看到控制台上打印出排序好了的名字。
使用swift非闭包来实现同样的功能,如下:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
print(reversed)
同样在控制台上打印出排序好了的名字,跟Java一样,但是我们可以看到即使是不使用闭包,swift还是要比java简洁一些,swift不愧为一个现代的语言,不止如此,swift的闭包还能够更简洁的语法来实现同样的功能。
闭包表达式语法(Closure Expression Syntax)
{ (parameters) -> returnType in
statements
}
用一个大括号把闭包表达式包围起来,小括号中放参数,箭头后面放返回值,in后面就是代码语句,跟函数类似,但是没有名字和func关键字,函数大括号写在返回类型的后面,闭包整个括起来。闭包表达式语法可以使用常量、变量和inout类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。
下面我们用闭包表达式来实现排序功能:
reversed = names.sort({(s1:String,s2:String) -> Bool in return s1>s2})
是不是比函数更简洁。
根据上下文推断类型(Inferring Type From Context)
因为排序闭包函数是作为sort(:)方法的参数传入的,Swift 可以推断其参数和返回值的类型。sort(:)方法被一个字符串数组调用,因此其参数必须是(String, String) -> Bool类型的函数。这意味着(String, String)和Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:
reversed = names.sort({s1,s2 in return s1>s2})
是不是更简洁。
单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)
单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
reversed = names.sort({s1,s2 in s1>s2})
更为简洁。
参数名称缩写(Shorthand Argument Names)
Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
reversed = names.sort({$0>$1})
足够简洁了,但还不是。
运算符函数(Operator Functions)
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现:
reversed = names.sort(>)
终极简洁。
尾随闭包(Trailing Closures)
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。我们使用这种方法来实现排序:
reversed = names.sort() { $0 > $1 }
把实现放到了函数的外面,增强了可读性。
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉:
reversed = names.sort { $0 > $1 }
不在多说。
捕获值(Capturing Values)
闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
详见:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html#capturing_values
闭包是引用类型(Closures Are Reference Types)
这个不多说,用过Java的都知道引用类型,闭包也是引用类型,这个比较好理解。
非逃逸闭包(Nonescaping Closures)
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
这个也没什么好说的,多练习才有用。
自动闭包(Autoclosures)
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
@autoclosure特性暗含了@noescape特性,如果你想让这个闭包可以“逃逸”,则应该使用@autoclosure(escaping)特性.