前言
Swift 是一门现代化的编程语言,它在许多方面都充满创新和便利性。在 Swift 中,闭包 是一项强大的特性,它为开发者提供了一种灵活、轻量级的函数式编程方式。本文我将总结一下我深入研究后关于 Swift中 闭包 的概念,种类和写法以及一些实际的应用。
一、闭包的概念
相信大家网上一搜都查到这么一句话:
闭包是一种自包含的功能代码块,是可以被传递和用作参数的函数。
那么这句话应该如何理解呢?需要理解这句话,需要首先理解 Swift 中函数是一等公民(First-Class Citizen)的概念。
所谓"第一等公民"(first class),指的是其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
而Swift 中的函数和闭包都是一等公民,因此闭包既可以被当作参数传递给函数,也可以赋值给变量,还可以作为函数的返回值。
二、闭包的种类
闭包有三种形式:全局函数、嵌套函数和闭包表达式。
1.全局函数
全局函数是一个特殊的闭包。他具有名字但不捕获任何值,定义在全局作用域内,例如以下代码:
swift
// 全局作用域中的函数
//`person是`greet` 函数的参数,而不是捕获的值。
func greet(person: String) -> String {
return "Hello, \(person)!"
}
let greeting = greet(person: "Alice")
print(greeting) // 输出: Hello, Alice!
2.嵌套函数
嵌套函数是在其他函数内部定义的函数,也是一个特殊的闭包,它具有名字,可以捕获其包含函数的参数和常量。嵌套函数在外部是不可见的,除非被包含的函数调用了它。
swift
func outerFunction() -> (String) -> String {
func innerFunction(name: String) -> String {
return "Welcome, \(name)!"
}
return innerFunction
}
let welcome = outerFunction()
let message = welcome("Bob")
print(message) // 输出: Welcome, Bob!
嵌套函数捕获的值会被保留在内存中,直到闭包不再被引用。从而保证了在下一次执行函数时,之前捕获的值依旧存在,也无需我们担心内存管理问题
swift
func outerFunction() -> () -> Void {
var value = 10
func innerFunction() {
value += 5
print(value)
}
return innerFunction
}
let closure = outerFunction()
closure() // 输出 15
closure() // 输出 20
3.闭包表达式
3.1 闭包表达式的定义和格式
闭包表达式是一种更为简洁的方式定义闭包,通常在需要时进行直接定义和使用,也可以捕获上下文的变量/常量。
闭包表达式的基本语法包括参数列表、返回箭头和代码块。格式如下:
swift
{ (parameters) -> returnType in
//statements
}
举2个例子,分别是 无参数无返回值 和 有参数有返回值(自己可以推导出另外两种写法: 无参数有返回值 和 有参数无返回值)
swift
//无参数无返回值-写法1
let log1: ()->Void = { ()->Void in
print("closure")
}
//无参数无返回值-写法2
let log2 = { ()->Void in
print("closure")
}
//调用方法
log1()
log2()
swift
//有参数有返回值-写法1
let log1: (_ str:String)->Void = { (str:String)->Void in
print(str)
return str
}
//有参数有返回值-写法2
let log2: (_ str:String)->Void = { (str:String)->Void in
print(str)
return str
}
//调用方法
let str1 = log1("closure")
let str1 = log2("closure")
3.2 闭包表达式的优化
在 Swift 中,闭包表达式具有简洁、灵活的语法,并支持一些优化方式。以下是闭包表达式的优化方式:
- 简化参数和返回类型
可以省略闭包参数和返回类型的声明,让 Swift 根据上下文进行类型推断。
swift
// 完整形式
let sum = { (a: Int, b: Int) -> Int in
return a + b
}
// 优化形式(优化了参数类型声明)
let sumOptimized = { a,b -> Int in
return a + b
}
用系统提供的排序函数sorted
更直观,因为该函数是对比两个Int参数,返回谁更大谁更小的布尔值判断,即
swift
{ (num1: Int, num2: Int) -> Bool in
return num1 < num2
}
所以可以省略闭包参数和返回类型的声明,如下:
swift
// 完整形式
let sortNumbers = numbers.sorted { (num1:Int, num2:Int) -> Bool in
return num1 < num2
}
// 优化形式(优化了参数类型和返回类型声明)
let sumSortNumbers = numbers.sorted { num1,num2 in
return num1 < num2
}
- 单表达式闭包
如果闭包只有一条语句,可以省略 return
关键字。
swift
// 完整形式
let square = { (number: Int) -> Int in
return number * number
}
// 优化形式
let squareOptimized = { (number: Int) -> Int in number * number }
- 参数名缩写
如果闭包体的参数和返回值可以被自动推断,那么可以使用 $0
, $1
, $2
, ... 来代替闭包中的参数名,从而省略 in
关键字。(如果闭包体只有一行,并且该行内容可以被自动推断,不仅能省略 return
关键字,甚至还可以省略 in
关键字)
swift
let numbers = [1, 2, 3, 4, 5]
// 完整形式
let mappedNumbers = numbers.map({ (number: Int) -> Int in
return number * 2
})
// 优化形式(闭包体只有一行,可以省略 `return` 关键字)
let mappedNumbersOptimized = numbers.map { $0 * 2 }
- 尾随闭包
在 Swift 中,如果函数的最后一个参数是闭包,而你又需要使用闭包作为函数的参数,那么就可以使用尾随闭包。
尾随闭包是一种在函数调用时编写的闭包表达式,它被写在函数调用的括号之后,尾随闭包的语法允许省略闭包的参数标签,让函数更加清晰易读。
swift
func performOperation(_ name:String, _ operation: () -> Void) {
// 执行操作
operation()
}
// 完整形式
performOperation("perform",{
print("Performing operation")
})
// 优化形式
performOperation("perform") {
print("Performing operation")
}
若函数只需要闭包表达式一个参数,当使用尾随闭包时,可以把()
也省略掉。
swift
func performOperation(_ operation: () -> Void) {
// 执行操作
operation()
}
// 完整形式
performOperation({
print("Performing operation")
})
// 优化形式
performOperation{
print("Performing operation")
}
三、逃逸闭包
逃逸闭包(Escaping Closures)是指在闭包被传递到函数之外的作用域,或者在函数执行完之后才被调用的闭包。(在 Swift 中,闭包默认是非逃逸的,即在函数结束前执行。)
需要使用 @escaping
标记逃逸闭包,以便在函数执行完毕后继续存在。
逃逸闭包通常用于异步操作,例如在后台执行任务后执行闭包。
swift
var completionHandlers: [() -> Void] = []
func doSomething(completion: @escaping () -> Void) {
completionHandlers.append(completion)
}
func executeCompletionHandlers() {
for handler in completionHandlers {
handler()
}
}
// 逃逸闭包
doSomething {
print("Task completed!")
}
// 执行逃逸闭包
executeCompletionHandlers() // 输出: Task completed!
在这个例子中,doSomething
函数接受一个逃逸闭包作为参数,并将其添加到 completionHandlers
数组中。稍后,通过调用 executeCompletionHandlers
函数,之前添加的闭包会被执行。
四、闭包的应用场景
闭包在 Swift 中有许多应用场景,其中一些包括:
1. 排序操作: 使用闭包对数组进行自定义排序。
swift
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
let sortedNumbers = numbers.sorted { $0 < $1 }
print(sortedNumbers) // 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
2. 函数式编程: 利用闭包进行函数式编程,实现高阶函数的功能。
swift
let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // 输出: [2, 4, 6, 8, 10]
3. UI 动画: 使用闭包实现 UIView 动画的 completion 部分。
swift
UIView.animate(withDuration: 1.0, animations: {
// 动画效果
}, completion: { _ in
// 动画完成后执行的闭包
print("Animation completed!")
})
总结
Swift 中的闭包是一项强大而灵活的特性,它为开发者提供了一种简洁、优雅的函数式编程方式。深入理解和熟练使用闭包,将有助于提高 Swift 编程的效率和代码质量。