Swift - 闭包(Closure)

510 阅读2分钟

在 Swift 中,闭包(Closure)是一种可以捕获并存储其上下文中变量和常量的自包含的功能代码块。闭包类似于匿名函数,可以在代码中作为参数传递、返回,或者作为变量、常量进行存储。

闭包是 Swift 中的核心概念,广泛用于异步编程、回调函数、集合操作等场景。

闭包的类型

闭包有三种主要形式:

  1. 全局函数:有名字的闭包,不能捕获上下文中的变量。
  2. 嵌套函数:有名字的闭包,能够捕获并存储上下文中的变量。
  3. 闭包表达式:轻量级语法,用于书写没有名字的匿名闭包。

闭包表达式的基本语法

闭包表达式语法是一种可以简洁编写闭包的方式。它的语法如下:

{ (参数列表) -> 返回类型 in
    // 闭包的代码体
}

例如,一个接受两个整数并返回它们和的闭包:

let sumClosure = { (a: Int, b: Int) -> Int in
    return a + b
}

let result = sumClosure(3, 5)  // result 为 8

闭包的简化语法

Swift 提供了很多闭包语法的简化形式,来让闭包更加简洁和易读。

  1. 推断参数和返回值类型
    • 如果闭包的参数和返回值类型可以从上下文推断出,就可以省略这些类型声明。
let sumClosure = { (a, b) in
    return a + b
}
  1. 单表达式闭包可以省略 return
    • 如果闭包体是单一表达式,则可以隐式返回该表达式的值,而不需要 return 关键字。
let sumClosure = { (a, b) in
    a + b  // 自动返回
}
  1. 参数名称的简写
    • Swift 提供了 $0$1$2 等参数名称的简写,表示闭包的第一个、第二个、第三个参数。
let sumClosure = {
    $0 + $1
}
  1. 尾随闭包
    • 如果闭包是函数的最后一个参数,可以将它写在括号外,这就是尾随闭包语法。
func performOperation(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

// 使用尾随闭包
let result = performOperation(a: 3, b: 5) {
    $0 + $1
}  // result 为 8

捕获值

闭包可以捕获并存储其定义上下文中的变量和常量。即使这些变量的作用域已经结束,闭包仍然可以在其代码体中访问和修改这些变量。

func makeIncrementer(amount: Int) -> () -> Int {
    var total = 0
    let incrementer: () -> Int = {
        total += amount
        return total
    }
    return incrementer
}

let incrementByTwo = makeIncrementer(amount: 2)
print(incrementByTwo())  // 输出 2
print(incrementByTwo())  // 输出 4

在这个例子中,incrementer 闭包捕获了 total 变量,即使 makeIncrementer 函数返回后,incrementByTwo 闭包仍然可以继续访问和修改 total 的值。

闭包的使用场景

  1. 回调和异步处理:闭包常用于异步代码中作为回调处理,例如网络请求、按钮点击事件等。
func fetchData(completion: @escaping (String) -> Void) {
    // 模拟异步请求
    DispatchQueue.global().async {
        let data = "Response Data"
        completion(data)
    }
}

fetchData { result in
    print(result)  // "Response Data"
}
  1. 函数式编程:闭包广泛用于 Swift 集合类型(如 mapfilterreduce)等函数式编程操作中。
let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }
print(squared)  // 输出 [1, 4, 9, 16, 25]

逃逸闭包

当一个闭包作为参数传递给一个函数时,通常会在函数体内立即执行。如果闭包在函数返回后才被执行,这样的闭包称为逃逸闭包,需要使用 @escaping 关键字标记。

func requestData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        completion("Data received")
    }
}

requestData { data in
    print(data)  // "Data received"
}

自动闭包(Autoclosure)

自动闭包是为了简化语法,将表达式自动封装为闭包传递。通常用于延迟执行操作,例如 assert 或逻辑短路操作。

func logIfTrue(_ condition: @autoclosure () -> Bool) {
    if condition() {
        print("True!")
    }
}

logIfTrue(2 > 1)  // 输出 "True!"

@autoclosure 自动将 2 > 1 表达式封装为闭包。

总结

闭包是 Swift 中功能强大的特性,允许我们编写简洁且高效的代码。它们可以捕获上下文中的变量,作为参数传递或返回,并支持多种简化语法形式。常见的使用场景包括回调、异步操作、函数式编程等。