Swift - 闭包



什么是 Swift 中的闭包?

闭包是自包含的功能块,可以在程序内部使用并执行指定的任务。闭包类似于 Objective-C 中的块或其他编程语言中的匿名函数。它们可以捕获并存储对其定义所在上下文中变量或常量的引用。此外,即使它们超出了原始作用域,它们也可以访问这些常量或变量的值。

Swift 中的闭包有三种形式:

  • 全局函数 - 它们是有名称的闭包,不捕获任何值。

  • 嵌套函数 - 在另一个函数内部定义的函数。它们有名称,可以捕获来自封闭函数的值。

  • 闭包表达式 - 使用此方法可以更简洁地编写闭包。我们可以编写未命名的闭包来捕获相邻块中的值。

闭包表达式

闭包表达式提供了一种编写内联闭包或内联和未命名函数的方法。它支持各种简短、优化和集中的语法来编写闭包,而不会失去其清晰度。因此,我们首先将看到闭包的基本语法,然后我们将转向 Swift 支持的其他表达式语法:

  • 从上下文中推断参数和返回值类型。

  • 单表达式闭包的隐式返回。

  • 简写参数名

  • 运算符方法

定义和调用基本闭包

在 Swift 中,我们可以简单地使用花括号 {} 来定义闭包。这些花括号包含闭包参数、返回类型(如果可用)、用于分隔参数和返回类型与主体部分的 `in` 关键字以及闭包的主体。闭包的参数可以是常规参数、输入输出参数和可变参数,但它们不包含默认值。元组也可以用作闭包中的参数和返回类型。我们可以通过为参数传递值(如果可用)来调用闭包。

语法

以下是定义接受参数并返回数据类型的闭包的通用语法:

{(parameters) -> return type in
   // body of closure
}

以下是调用闭包的语法。

closure(parameters)

示例

Swift 程序演示了一个没有参数的闭包。

// Creating a Closure
let studname = { print("Welcome to Swift 4 Closures") }

// Calling a closure
studname()
输出

它将产生以下输出:

Welcome to Swift 4 Closures

示例

Swift 程序演示了一个带有参数的闭包。

// Closure with parameters
let divide = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}

// Calling closure
let result = divide(200, 20)
print (result)
输出

它将产生以下输出:

10

从上下文中推断类型

闭包也可以作为内联闭包表达式传递到函数或方法中,因此我们可以推断其参数和返回值的类型。这意味着我们不需要显式编写闭包中传递的参数类型和闭包返回的值类型,编译器将根据闭包的使用上下文自动推断闭包的类型。

示例

Swift 程序将闭包作为参数传递给函数。

// Define an array of String 
let myValues = ["Mohina", "Suman", "Mohit"]

// Use the 'map' function to add the given string in all the elements of the array 
/* The type of closure is inferred according to the fact that 'map()' is 
applied to an array of strings. So here the closure adds a specified string to each element
hence the inferred type of the closure is (String) -> String*/
let newArray = myValues.map { $0 + " Hey" }

print(newArray) 
输出

它将产生以下输出:

["Mohina Hey", "Suman Hey", "Mohit Hey"]

单表达式闭包的隐式返回

在闭包中,单一表达式可以隐式地返回一个表达式,而无需显式使用 `return` 关键字。或者我们可以说,如果闭包只包含一条语句,则它可以返回一个表达式而无需指定返回类型。这使语法更简洁易读。

示例

Swift 程序从单表达式闭包隐式返回表达式。

// Single line closure without return type
let add: (Int, Int) -> Int = { a, b in
   a + b
}

let output = add(5, 6)
print("Addition:", output) 
输出

它将产生以下输出:

Addition: 11

简写参数名

在使用内联闭包时,我们可以使用 $0、$1、$2 等名称来编写闭包参数的值,而不是为它们命名。这是在闭包中表达参数的最短方法。其中 $0 指的是第一个参数,$1 指的是第二个参数,$2 指的是第三个参数,依此类推。

如果我们使用这些简写参数名,那么我们可以从定义部分移除闭包参数列表。编译器将根据预期的函数类型自动推断参数的类型。我们还可以移除 `in` 关键字,因为简写参数是在表达式主体中定义的。

示例

Swift 程序演示了闭包中的简写参数名。

// Creating a closure
var shorthand: (String, String) -> String

// Assigning the second parameter and discarding the first parameter
shorthand = { $1 }

// Calling the closure with two arguments will return the second parameter
print(shorthand("100", "200"))
输出

它将产生以下输出:

200

运算符方法

Swift 提供了一种简单的方法来访问成员,只需将运算符函数作为闭包即可。或者我们可以说,使用闭包,我们可以通过重载它们来定义运算符的行为。

示例

Swift 程序演示了闭包中的运算符方法。

// Define a custom operator method for addition numbers 
func + (left: (Double, Double), right: (Double, Double)) -> (Double, Double) {
   return (left.0 + right.0, left.1 + right.1)
}

// Using the custom operator in a closure
let addNumbers: ((Double, Double), (Double, Double)) -> (Double, Double) = { $0 + $1 }

let num1  = (3.0, 3.0)
let num2 = (5.0, 2.0)

// Adding the values using addNumbers closure
let result = addNumbers(num1, num2)
print("Resultant Sum: \(result)")
输出

它将产生以下输出:

Resultant Sum: (8.0, 5.0)

尾随闭包

尾随闭包是 Swift 中的一种特殊类型的闭包。当闭包定义在函数括号 () 之外时,特别是当闭包是函数的最后一个参数时,这种类型的闭包被称为尾随闭包。

这种类型的闭包通常用于闭包很长且无法内联编写的情况。如果函数只包含闭包作为参数,那么在调用函数或方法时,我们可以移除括号 (),例如,`names.map{$1 = $0}`。

语法

以下是尾随函数的语法:

// Function that takes closure
func functionName(closure:()->void){
  // Function body
}

// Calling function without trailing closure
functionName(closure:{// closure body})

// Calling function with trailing closure
functionName(){// closure body}

示例

Swift 程序演示了尾随闭包。

// Function to operate on two numbers using a trailing closure
func operation(_ x: Int, _ y: Int, op: (Int, Int) -> Int) -> Int {
   return op(x, y)
}

// Using trailing closure to add two numbers
let res = operation(8, 9) { (a, b) in
   return a + b
}
print("Sum: \(res)")

输出

它将产生以下输出:

Sum: 17

尾随函数在高阶函数(如 map、filter 或 sort)中很常见,其中闭包充当回调或转换。

示例

Swift 程序演示了高阶函数中的尾随闭包。

// Array of string
let names = ["Mohan", "Mohit", "Roy", "Suman"]

// Calling map() function with trailing function
let lowercaseNames = names.map { $0.lowercased() }

// Displaying the names in lowercased
print(lowercaseNames) 

输出

它将产生以下输出:

["mohan", "mohit", "roy", "suman"]

单个函数可以有多个尾随闭包,我们可以移除第一个尾随闭包的参数标签,并为其余的尾随闭包添加标签。

示例

Swift 程序演示了函数中的多个尾随闭包。

// Function with multiple trailing closures
func Operations(_ x: Int, _ y: Int, op1: (Int, Int) -> Int, op2: (Int, Int) -> Int) -> (Int, Int) {
   let result1 = op1(x, y)
   let result2 = op2(x, y)
   return (result1, result2)
}

// Using multiple trailing closures
var output = Operations(11, 6, op1: { $0 + $1 }, op2: { $0 * $1 })

print(output)

输出

它将产生以下输出:

(17, 66)

捕获值和引用类型

在 Swift 中,捕获常量和变量值是通过闭包实现的。它进一步引用和修改闭包主体中这些常量和变量的值,即使定义变量或常量的原始作用域不再存在。

将函数或闭包赋值给常量或变量时,我们将该常量或变量设置为对该函数或闭包的引用。这意味着如果我们将闭包赋值给两个常量或变量,则这两个常量或变量都引用同一个闭包。

示例

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 100
   func decrementer() -> Int {
      overallDecrement -= total
      print(overallDecrement)
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 18)
print(decrem())
print(decrem())
print(decrem())

输出

它将产生以下输出:

82
82
64
64
46
46

每次调用外部函数 `calcDecrement` 时,它都会调用 `decrementer()` 函数,将值减 18 并通过外部函数 `calcDecrement` 返回结果。这里 `calcDecrement` 充当闭包。

即使函数 `decrementer()` 没有参数,闭包默认也会通过捕获其现有值来引用变量 `overallDecrement` 和 `total`。指定变量的值的副本将与新的 `decrementer()` 函数一起存储。Swift 通过在变量不用时分配和释放内存空间来处理内存管理函数。

广告
© . All rights reserved.