1.概念
Swift 中的闭包是自包含的代码块,可以在代码中传递和使用。它们可以捕获和存储其上下文中的任何常量和变量引用。
2.表达式语法
闭包的完整形式为:
{ (parameters) -> returnType in
statements
}
这里,参数列表和返回类型可选。例如,一个没有参数和返回值的简单闭包示例:
let greet = {
print("Hello, world!")
}
greet()
当我们需要向闭包传递参数时,可以在参数列表中定义它们:
let add = { (a: Int, b: Int) -> Int in
return a + b
}
let sum = add(5, 7)
print(sum) // 输出:12
将闭包作为参数传递
闭包可以像普通变量一样传递给函数。以下是一个接受简单闭包作为参数的函数:
func perform(operation: () -> Void) {
operation()
}
perform {
print("Performing an operation.")
}
闭包简写
在 Swift 中,当闭包作为参数传递给函数时,可以利用语言特性对闭包进行简写。以下列举了闭包简写的几种方法:
- 参数类型推断:当闭包作为函数的参数时,Swift 会自动推断闭包参数的类型。因此,在闭包表达式中,无需明确指定参数类型。例如:
func doMath(_ operation: (Int, Int) -> Int) {
let result = operation(5, 3)
print(result)
}
doMath { a, b in
a * b
} // 输出:15
在上述示例中,我们可以省略闭包中的参数类型 Int
,因为 Swift 根据 doMath
函数类型推断出闭包参数的类型。
- 隐式返回:针对只包含一个表达式的闭包,可以不使用
return
关键字,因为这个表达式会作为闭包的“隐式返回值”。例如:
let sortedNumbers = [5, 2, 1, 8, 4, 7].sorted { a, b in
a < b
}
在这个示例中,我们省略了 return
关键字,因为闭包只包含一个表达式:a < b
。Swift 将此表达式的结果视为隐式返回值。
- 简写参数名:当闭包参数的名字未定义时,可以使用
$0
,$1
,$2
等编号引用闭包参数。例如:
let sortedNumbers = [5, 2, 1, 8, 4, 7].sorted { $0 < $1 }
在这个示例中,我们使用简写参数名 $0
和 $1
表示闭包的第一个和第二个参数。这样就不需要显式地定义参数名(例如 a, b
等)。
- 尾随闭包(Trailing closure):当函数的最后一个参数是一个闭包时,可以将闭包表达式移出函数括号,并跟在函数参数列表之后。这种写法在闭包表达式较长或者闭包作为主要逻辑参数时,使代码更具可读性。例如:
示例 1. 使用 map
函数
let numbers = [1, 2, 3, 4, 5]
// 使用尾随闭包语法
let squaredNumbers = numbers.map { $0 * $0 }
// 标准闭包语法
let squaredNumbersStandard = numbers.map({ $0 * $0 })
在这个示例中,使用尾随闭包语法让代码看起来更简洁。map
函数的最后一个参数是一个闭包,所以我们可以将闭包移到括号外部。
示例 2. 使用 async
函数
DispatchQueue.main.async {
print("Hello, trailing closure!")
}
// 标准闭包语法
DispatchQueue.main.async(execute: {
print("Hello, standard closure!")
})
在这个示例中,async
函数的最后一个参数也是一个闭包,我们将其移出参数列表,使用尾随闭包简化代码。
逃逸闭包(Escaping Closures)
逃逸闭包(escaping closure)是 Swift 中闭包的特殊类型,它指的是传递给函数的闭包会在函数返回后执行。
3.注意事项
逃逸闭包可能导致循环引用(retain cycle)问题。当闭包在函数之外执行,尤其是在闭包和类实例之间产生相互引用时,需要特别关注内存管理。这时,应使用捕获列表(capture list),指定捕获方式为 weak
或 unowned
。