闭包定义

闭包(Closure)是自包含的函数代码块,可以在代码中被传递和使用。swift 中的闭包和 c 或 oc 中的 block 以及其他的编程语言的匿名函数类似。

在 swift 中闭包的范围比较广泛,不只是 oc 中 block 这种的,还包含像上节中讲到的全局函数和嵌套函数,全局函数是一种有名字但不会捕获任何只的闭包。嵌套函数是一种有名字可以捕获其封闭函数块中的值。

一般闭包表达式

类似于 OC 的 block 的一般闭包表达式

// 闭包的声明
var closure: ((String, Int) -> String)!

// 闭包的实现
closure = { (name, age) in
    return "\(name)是\(age)岁"
}

// 闭包的调用
print(closure("小明", 15))

闭包声明:是将各个参数的类型抽出来如果没有参数用()表示,如果没有返回值用->()或者->Void表示。

闭包的实现:闭包实现整体用大括号包起来,参数写在 in 之前 有返回值则用 return 返回。

闭包的调用:和 oc 中的调用方式一样。

不再举过多例子了,这里的闭包只是比较基本的表达方法,其余无参无返,有参无返,无参有返,表达方式可以参考上节的函数的表达方式。

闭包进阶

以排序sorted(by:)为例

常规的用法:

var arr = ["2", "3"]

arr = arr.sorted(by: { (str1, str2) -> Bool in
    return str1 > str2
})

print(arr) //["3", "2"]

这是一个比较简单的闭包,只有默认的两个参数,并且只是一个简单的返回比较大小的处理,实际上还可以这么写

var arr = ["2", "3"]

arr = arr.sorted(by: {$0 > $1})

print(arr) //["3", "2"]

隐式返回:swift 中的闭包是支持直接将表达式的返回值作为闭包的返回值。

参数压缩:$0,$1...分别代表了第一个参数和第二个参数…。

还可以更简单

var arr = ["2", "3"]

arr = arr.sorted(by: >)

print(arr) //["3", "2"]

这里的>不止是表面上的一个>而是一个运算符方法

尾随闭包

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
}
 
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
    // 闭包主体部分
})
 
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
    // 闭包主体部分
}

闭包值捕获

在闭包中和嵌套函数中都可以捕获上一层函数体中的变量

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

逃逸闭包

闭包默认不允许逃逸出当前闭包体,如果需要逃逸则要加@escaping来表明这个这个闭包可以逃逸

逃逸:是指这个闭包需要脱离当前函数体,例如作为参数传给另一个函数,或者在另个闭包体内调用这个闭包。

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

自动闭包

@autoclosure仅作了解,使用场景不多,用在需要延迟执行方法的时候,下例中可以用自动闭包使 serve 当做方法接收 String 类型的参数.

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))