美文网首页
Swift-闭包表达式

Swift-闭包表达式

作者: 小驴拉磨 | 来源:发表于2018-06-08 11:06 被阅读9次

    闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。
    闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。
    函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:

    • 全局函数是一个有名字但不会捕获任何值的闭包

    • 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包

    • 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
      Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

    • 利用上下文推断参数和返回值类型

    • 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字

    • 参数名称缩写

    • 尾随闭包语法

    闭包表达式

    闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 sorted(by:) 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。

    sorted 方法

    Swift 标准库提供了名为 sorted(by:) 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

    下面的闭包表达式示例使用 sorted(by:) 方法对一个 String 类型的数组进行字母逆序排序。以下是初始数组:

    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    

    sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。

    该例子对一个 String 类型的数组进行排序,因此排序闭包函数类型需为 (String, String) -> Bool。

    提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为 sorted(by:) 方法的参数传入:

    func backward(_ s1: String, _ s2: String) -> Bool {
        return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    // reversedNames 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
    

    如果第一个字符串(s1)大于第二个字符串(s2),backward(::) 函数会返回 true,表示在新的数组中 s1 应该出现在 s2 前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母 "B" 大于字母 "A" ,字符串 "Tom" 大于字符串 "Tim"。该闭包将进行字母逆序排序,"Barry" 将会排在 "Alex" 之前。

    然而,以这种方式来编写一个实际上很简单的表达式(a > b),确实太过繁琐了。对于这个例子来说,利用闭包表达式语法可以更好地构造一个内联排序闭包。

    闭包表达式语法

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

    { (parameters) -> returnType in
        statements
    }
    

    闭包表达式参数 可以是 in-out 参数,但不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。元组也可以作为参数和返回值。
    下面的例子展示了之前 backward(::) 函数对应的闭包表达式版本的代码:

    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in  return s1 > s2})
    

    需要注意的是内联闭包参数和返回值类型声明与 backward(::) 函数类型声明相同。在这两种方式中,都写成了 (s1: String, s2: String) -> Bool。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。

    • 根据上下文推断类型
      因为排序闭包函数是作为 sorted(by:) 方法的参数传入的,Swift 可以推断其参数和返回值的类型。sorted(by:) 方法被一个字符串数组调用,因此其参数必须是 (String, String) -> Bool 类型的函数。这意味着 (String, String) 和 Bool 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:
    reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
    
    • 单表达式闭包隐式返回
      单行表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
    reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
    

    在这个例子中,sorted(by:) 方法的参数类型明确了闭包必须返回一个 Bool 类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回 Bool 类型值,因此这里没有歧义,return 关键字可以省略。

    • 参数名称缩写
      Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 0,1,$2 来顺序调用闭包的参数,以此类推。

    如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

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

    在这个例子中,0和1表示闭包中第一个和第二个 String 类型的参数。

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

    原文出自51Swift转载请保留原文链接

    相关文章

      网友评论

          本文标题:Swift-闭包表达式

          本文链接:https://www.haomeiwen.com/subject/vkpbsftx.html