Swift 闭包

2020-06-30  本文已影响0人  懒床小番茄

一个函数和它所捕获的变量\常量环境组合起来,称为闭包

一、闭包表达式

  闭包表达式是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。

Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

1、在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数

 func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
var fn = {
    (v1: Int, v2: Int) -> Int in return v1 + v2
 }
fn(10, 20)

闭包表达式的语法形式如下:

{
   (参数列表) -> 返回值类型 in 函数体代码
}

2、闭包表达式的简写

以数组的排序方法 sort(by: ) 作为示例
sort(by: )会基于你提供的排序闭包表达式的判断结果对数组中的值(类型确定)进行排序。

//定义一个数组
var arr = [2,4,1,45,23,12,43]
func temp(v1: Int, v2: Int) -> Bool {
    return v1 >= v2
}
arr.sort(by: temp)
//temp函数对应的闭包表达式形式:
    arr.sort { (v1: Int, v2: Int) -> Bool in
        return v1 >= v2
    }
//根据上下文推断参数类型和返回类型
    arr.sort(by: {v1, v2 in return v1 >= v2})
//单表达式闭包的隐式返回 单行表达式闭包可以通过省略 return 
    arr.sort(by: {v1, v2  in v1 >= v2 })
//参数名称缩写 Swift自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0,$1,$2... 来顺序调用闭包的参数。
    arr.sort(by: {$0 >= $1})
//运算符方法
    arr.sort(by: >=)
//尾随闭包 如果将一个闭包表达式作为函数的最后一个参数,使用尾随闭包可以增强可读性
    arr.sort(){$0 >= $1}
//如果闭包表达式作为函数的唯一实参,并且使用了尾随闭包,则函数名后面的小括号可以省略
    arr.sort{$0 >= $1}
print(arr)

二、逃逸闭包

  当闭包作为一个实际参数传递给一个函数的时候,并且它会在函数返回之后调用,我 们就说这个闭包逃逸了。当你声明一个接受闭包作为形式参数的函数时,你可以在形 式参数前写 @escaping 来明确闭包是允许逃逸的。
  闭包可以逃逸的一种方法是被储存在定义于函数外的变量里。比如说,很多函数接收 闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到 任务完成——闭包需要逃逸,以便于稍后调用。

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

  someFunctionWithEscapingClosure() 函数接收一个闭包作为参数,同时该闭包被添加到一个函数外定义的数组中。此时你需要将这个参数标记为 @escaping ,否则将会编译报错。
  让闭包 @escaping 意味着你必须在闭包中显式地引用 self。例如下面代码,传递到someFunctionWithEscapingClosure()的是一个被@escaping标识的逃逸闭包,所以必须要显现的引用self,即必须要写self;而传递到someFunctionWithNonescapingClosure()的是一个非逃逸闭包,所以可以隐式的引用self,即可以不写self。

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}
class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}
let instance = SomeClass()
instance.doSomething()
print(instance.x) // 打印出“200”
completionHandlers.first?()
print(instance.x) // 打印出“100”

三、自动闭包

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

func getNum() -> Int{
    let a = 10
    let b = 11
    print("调用")
    return a + b
}
// 如果第1个数大于0,返回第一个数。否则返回第2个数
func getFirstPositive(v1: Int, v2: Int) -> Int{
    return v1 > 10 ? v1 : v2
}
getFirstPositive(v1: 10, v2: getNum())

  例如上述代码,当调用 getFirstPositive() 函数时,不管传入的v1是否大于10,则都会调用getNum()函数,这样就造成了浪费。当时如果将参数v2改成函数类型的参数,则可以让v2延迟加载

func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int{
    return v1 > 10 ? v1 : v2()
}
//getFirstPositive(10, {20})
//因闭包作为函数的最后一个参数,可以使用尾随闭包
getFirstPositive(10){20}

  通过 @autoclosure 标志标记它的形式参数使用了自动闭包。现在你可以调用函数就像它接收了一个 Int类型的 实际参数而不是闭包。实际参数自动地转换为闭包,因为 customerProvider 形式参数的类型被标记为 @autoclosure 标记。

func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int{
    return v1 > 10 ? v1 : v2()
}
//因为使用了@autoclosure进行标记它的形式参数使用了自动闭包,调用的时候不再是//getFirstPositive(10, {20})
getFirstPositive(10, 20)

注意:

如果有什么不对的地方,欢迎指正,大家共同进步

上一篇 下一篇

猜你喜欢

热点阅读