闭包

闭包定义

闭包是自包含的函数代码块,可以在代码中被传递和使用。

闭包可以捕获和存储所在上下文任意的常量和变量的引用

全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一

  • 全局函数是一个有名字但不会捕捉任何值的闭包
  • 嵌套函数是一个有名字可以捕获其封闭函数域内值的闭包
  • 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量的匿名闭包

闭包表达式语法

1
2
3
4
//闭包表达式语法有如下的一般形式:
{ (parameters) -> return type in
statements
}

闭包表达是可以是in-out参数,但不能设定默认值

单表达式闭包隐式返回

单行表达式闭包可以省略 return关键字来隐士的返回单行表达式的结果。

sorted(by:{s1, s2 in s1 > s2})
因为这里只包含了一个单一表达式 s1>s2

参数名称缩写

Swift自动为内联函数提供了参数名称缩写功能,直接通过$0,$1,$2 来顺序调用闭包的参数。如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成

1
var reversert = names.sorted(by,{ $0 < $1 })
运算符函数

实际上还有一种更简短的方式来编写上面列子中的闭包表达式。Swift的String类型定义了 (>) 的字符串实现,其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与 sorted(by:)方法的参数需要的函数类型符合。因此,你可以使用更简单的传递一个大于号,Swift可以自动推断出你想使用大于号的字符串的函数实现

1
var reversert = names.sorted(by:>)

尾随闭包:

尾随闭包是一个写在函数括号之后的闭包表达式,函数将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:

1
2
3
someFunctionThatTakesAClosure(){
//闭包主体
}

闭包是引用类型

逃逸闭包:

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回后才被执行,我们称该改闭包从函数中逃逸。@escaping 标记这个闭包是允许“逃逸”出这个函数的

1
2
3
4
var completeionHandles: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void){
completeionHandles.append(completionHandler)
}

将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用

1
2
3
4
5
6
7
8
9
10
11
func someFunctionWithNonescapingClosure(closure: () -> Void){
closure()
}

class SomeClass{
var x = 10
func doSomething(){
someFunctonWithEscapingClosure{ self.x = 100 }
someFunctionWithNonescapingClosure{ x = 200 }
}
}

自动闭包

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候会返回被包装在其中的表达式

自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行

1
2
3
4
5
6
7
var customersInLine = ["Chris","Alex", "Ewa", "Barry","Daniella]
print(customersInLine.count)
// 打印出 "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出 "5"
print("Now serving")

如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure@escaping属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var customersInLine = ["Barry","Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String){
customerProviders.append(customerProvider)
}

collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// 打印 "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// 打印 "Now serving Barry!"
// 打印 "Now serving Daniella!"
0%