首页 > 代码库 > Swift学习笔记七:闭包

Swift学习笔记七:闭包

闭包可以 捕获 和存储其所在上下文中任意常量和变量的引用。 Swift 会为您管理在 捕获 过程中涉及到的内存操作。
在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
1. 全局函数是一个有名字但不会捕获任何值的闭包
2. 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
3. 闭包表达式是一个可以捕获其上下文中变量或常量值的没有名字的闭包
一、闭包表达式

     闭包函数类似于Objective-C中的block。下面我们用事实说话:

let counts = [3,2,5,1,6]
//********** 方法1:使用普通函数*******
func sortInc(item1:Int,item2:Int) ->Bool{
    return item1 < item2
}
sort(counts, sortInc)
//********** 方法2:使用闭包函数*******
sort(counts, {(a:Int,b:Int) -> Bool in return a<b})

    Swift 标准库提供了 sort 函数,会根据您提供的排序闭包将已知类型数组中的值进行排序。
          方法1中使用的是普通函数,方法2传入的是一个闭包,在这里,这2个方法最终的效果是一样的。但是明显的,方法2写起来比方法1要简单
     1)闭包表达式语法

{ (parameters) -> returnType in      statements  }  

关键:闭包表达式主要分为3个部分
               ①闭包表达式所有内容都由大括号{}括起来
               ②闭包表达式关键字in前面部分和函数的定义是一样的。只是去掉了func和函数名
               ③闭包表达式关键字in后面部分是函数的具体代码
     2) 根据上下文推断类型
          因为排序闭包是作为函数的参数进行传入的,Swift可以推断其参数和返回值的类型。上面的方法2中的代码可以改为下面的形式:

 sort(counts, {a,b inreturn a<b})

     3)单一表达式闭包可以省略 return
            单一表达式闭包可以通过隐藏 return 关键字来隐式返回单行表达式的结果。上面的代码还可以省略:

sort(counts, {a,b in a<b})

这个例子中,sort第二个参数必须返回一个Bool值,因为闭包只包含一个单一表达式a<b,并且表达式返回Bool类型,所以return可以省略

      4)参数名称缩写
               Swift 会自动为内联函数提供了参数名称缩写功能,您可以直接通过 $0,$1,$2 来顺序调用闭包的参数。
               如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。 in 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成,上面的例子可以简化成这样
           sort(counts, {$0<$1})   //$0 $1表示闭包中第一个和第二个Int类型的参数。
     看!Swift的闭包多洋气啊,竟然可以简化成这样


     5)运算符函数
          实际上还有一种更简单的方法,Swift中<符号可以作为一个函数来使用
                     sort(counts, <)
  

2. Trailing 闭包
          如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用 trailing 闭包来增强函数的可读性。 Trailing 闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用。上面的例子用Trailing 闭包形式表达如下
               sort(counts){$0<$1}   //$0 $1表示闭包中第一个和第二个Int类型的参数。

          注意:如果函数只需要闭包表达式一个参数,当您使用 trailing 闭包时,您甚至可以把 () 省略掉。

3. 捕获 (Caputure)
          闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

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

注意:
               Swift 会决定捕获引用还是拷贝值。 您不需要标注 amount 或者 runningTotal 来声明在嵌入的 incrementor 函数中的使用方式。 Swift 同时也处理 runingTotal 变量的内存管理操作,如果不再被 incrementor 函数使用,则会被清除。
          2) 闭包是引用类型
               函数和闭包都是引用类型。闭包的赋值都是指针赋值。这意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:

func makeIncrementor (forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
let a = incrementByTen
这里a和incrementByTen都是指针,都他们中一个的值改变,另一个会随着一起改变