iOS - Swift Closure 闭包

时间:2023-03-09 01:30:58
iOS - Swift Closure		闭包

1、Closure

  • 闭包在 Swift 中非常有用。通俗的解释就是一个 Int 类型里存储着一个整数,一个 String 类型包含着一串字符,同样,闭包是一个包含着函数的类型。有了闭包,你就可以处理很多在一些古老的语言中不能处理的事情。这是因为闭包使用的多样性,比如你可以将闭包赋值给一个变量,你也可以将闭包作为一个函数的参数,你甚至可以将闭包作为一个函数的返回值。它的强大之处可见一斑。在 Swift 的很多文档教材中都说函数是“一等公民”,起初我还不是很理解“一等公民”是什么意思,但当我理解了闭包以及它的强大功能后,我恍然大悟、茅塞顿开、醍醐灌顶。原来闭包的这些特性就是“一等公民”的特性啊!

  • 闭包是功能性自包含模块,可以在代码中被传递和使用。一段程序代码通常由常量、变量和表达式组成,然后使用一对花括号“{}” 来表示闭合并包裹着这些代码,由这对花括号包裹着的代码块就是一个闭包。Swift 中的闭包与 C 和 Objective-C 中的 Block 以及其他一些编程语言中的 lambdas 比较相似。Block 和闭包的区别只是语法的不同而已,而且闭包的可读性比较强。

  • 闭包是引用类型,无论你将函数/闭包赋值给一个常量还是变量,实际上都是在将常量/变量设置为对应函数/闭包的引用,这也意味着如果你将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包。

  • 1)在 Swift 语言中有三种闭包形式:

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

        func myConpare(s1:String, s2:String) -> Bool {
    
            return s1 > s2
        }
    
        let namesArray:Array = ["Jill", "Tim", "Chris"]
    
        let names = namesArray.sort(myConpare)
  • 3)一般形式:

        { (parameters参数) -> returnType返回类型 in
    
            statements
        }
    • 可以使用常量、变量、inout、可变参数、元组类型作为闭包的参数,但不能在闭包参数中设置默认值,定义返回值和函数返回值的类型相同。

    • 闭包表达式中的 in 关键字表示闭包的参数和返回值类型定义已经完成,这些参数和返回值都将在下面的闭包函数体中得到处理。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1:String, s2:String) -> Bool in
      
              return s1 > s2
          }
  • 4)参数类型隐藏形式:

    • Swift 中有类型推断的特性,所以我们可以去掉参数类型。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) -> Bool in
      
              return s1 > s2
          }
  • 5)返回值类型隐藏形式:

    • Swift 中有类型推断的特性,所以我们可以去掉返回值类型。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) in
      
              return s1 > s2
          }
  • 6)return 隐藏形式:

    • 单行表达式的闭包可以通过隐藏关键字 return 来隐式地将单行表达式的结果作为返回值。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) in
      
              s1 > s2
          }
  • 7)参数名省略形式:

    • 闭包的使用非常的灵活,我们可以省略闭包参数列表中的参数的参数类型定义,被省略的参数类型会通过闭包函数的类型进行推断。同时,我们也可以在闭包函数体中通过使用闭包的参数名简写功能,直接使用 $0、$1、$2 等名字就可以引用的闭包参数值。如果同时省略了参数名和参数类型,那么 in 关键字也必须被省略,此时闭包表达式完全由闭包函数体构成。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort {
      
              $0 > $1
          }
  • 8)trailing 闭包形式:

    • 闭包可以做其他函数的参数,而且通常都是函数的最后一个参数。但是如果作为参数的这个闭包表达式非常长,那么很有可能会影响函数调用表达式的可读性,这个时候我们就应该使用 trailing 闭包。trailing 闭包和普通闭包的不同之处在于它是一个书写在函数参数括号之外(之后)的闭包表达式,函数会自动将其作为最后一个参数调用。

    • 当函数有且仅有一个参数,并该参数是闭包时,不但可以将闭包写在 () 外,还可以省略 ()。Swift 2.2 中可以不管参数的个数完全省略 ()。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort() {
      
              $0 > $1
          }
  • 9)闭包捕获:

    • 闭包可以在其定义的上下文中捕获常量或变量,即使定义这些常量或变量的原作用域已经不存在,仍然可以在闭包函数体内引用和修改这些常量或变量,这种机制被称为闭包捕获。在 Swift 中闭包的最简单形式是嵌套函数。嵌套函数就可以捕获其父函数的参数以及定义的常量和变量,全局函数可以捕获其上下文中的常量或变量。

          func increment(amount: Int) -> (() -> Int) {
      
              var total = 0
      
              func incrementAmount() -> Int {
      
              // total 是外部函数体内的变量,这里是可以捕获到的
                  total += amount
      
                  return total
              }
      
              // 返回的是一个嵌套函数(闭包)
              return incrementAmount
          }
      
          // 闭包是引用类型,所以 incrementByTen 声明为常量也可以修改 total
          let incrementByTen = increment(10)
          incrementByTen()    // return 10,incrementByTen 是一个闭包
      
          // 这里是没有改变对 increment 的引用,所以会保存之前的值
          incrementByTen()    // return 20
          incrementByTen()    // return 30
      
          let incrementByOne = increment(1)
          incrementByOne()    // return 1,incrementByOne 是一个闭包
          incrementByOne()    // return 2
          incrementByTen()    // return 40
          incrementByOne()    // return 3