Swift 闭包的多种形式:包括闭包表达式、尾随闭包、逃逸闭包等
创始人
2025-07-11 13:30:34
0

本文我们将介绍 Swift 中闭包表达式、尾随闭包和逃逸闭包等相关的内容。如果你尚未安装 Xcode 和配置 Swift 开发环境,请您先阅读这篇文章。

接下来,我们启动 Xcode,然后选择 "File" > "New" > "Playground"。创建一个新的 Playground 并命名为 "Closures"。

在 Swift 中,闭包是自包含的功能块,可以捕获和存储上下文中任意常量和变量的引用。Swift 闭包有多种形式,包括闭包表达式、尾随闭包、逃逸闭包等。

闭包表达式

闭包表达式语法

闭包表达式是一种轻量级语法,用于表示内联闭包。它的语法如下:

{ (parameters) -> returnType in
    // Closure body
}

相关说明如下:

  • parameters:定义了闭包的参数列表。
  • returnType:定义了闭包的返回类型。
  • in:用于分隔参数列表和闭包体。

创建闭包

了解了闭包表达式的语法之后,我们来创建一个闭包。

Swift Code

// 定义一个闭包,它接受两个参数并返回它们的总和
let addClosure: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
    return a + b
}

// 调用 addClosure 闭包
let sum = addClosure(2, 3)
print("Sum: \(sum)")

// Output: Sum: 5

在以上例子中,addClosure 是一个接受两个整数参数并返回它们和的闭包。在闭包表达式中,Swift 可以根据上下文推断参数类型和返回类型,因此通常可以省略它们:

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

如果闭包体只包含一条语句,可以省略 return 关键字:

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

在 Swift 的闭包表达式中,$0$1 等是用来表示闭包参数的缩写形式。这种缩写形式允许在闭包表达式中直接引用参数,而不需要显式地命名。所以,以上的代码,还可以继续简化:

let addClosure: (Int, Int) -> Int = { $0 + $1 }

下面,我们来看一下等价的 TypeScript 代码。

TypeScript Code

const addClosure: (a: number, b: number) => number = (a, b) => {
    return a + b;
};

const sum: number = addClosure(2, 3);
console.log(`Sum: ${sum}`); 

// Output: "Sum: 5"

闭包作为函数参数

闭包可以作为函数的参数,使得函数更加灵活。

Swift Code

func performOperation(_ operation: (Int, Int) -> Int, a: Int, b: Int) {
    let result = operation(a, b)
    print("Result: \(result)")
}

performOperation({ (a: Int, b: Int) -> Int in
    return a + b
}, a: 2, b: 3)

// Output:Result: 5

TypeScript Code

function performOperation(operation: (a: number, b: number) => number, a: number, b: number): void {
    const result: number = operation(a, b);
    console.log(`Result: ${result}`);
}

performOperation((a: number, b: number) => {
    return a + b;
}, 2, 3);

// Output: "Result: 5"

闭包作为排序函数的参数

Swift Code

let numbers = [4, 2, 8, 5, 1]
// 使用尾随闭包
let sortedNumbers = numbers.sorted { $0 < $1 }
print(sortedNumbers)

// Output: [1, 2, 4, 5, 8]

TypeScript Code

const numbers: number[] = [4, 2, 8, 5, 1];
const sortedNumbers: number[] = numbers.sort((a, b) => a - b);

console.log(sortedNumbers);
// Output: [1, 2, 4, 5, 8]

在闭包中捕获值

在 Swift 中,闭包可以捕获并存储它们定义时所在上下文中的常量和变量。即使定义这些常量和变量的原始上下文已经不存在,闭包仍然可以引用和修改这些值。下面我们来举一个闭包捕获外部函数的局部变量的示例:

Swift Code

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

let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // Output: 10
print(incrementByTen()) // Output: 20

在以上代码中,incrementByTen 是一个闭包,它捕获了 makeIncrementer 函数内的 runningTotal 和 amount 变量。即使 makeIncrementer 函数的执行已经完成,这些捕获的变量仍然存在于闭包内。

在 JavaScript 中,函数可以访问定义它们的函数的作用域中的变量。这个特性被称为词法作用域或静态作用域。虽然 JavaScript 没有专门的闭包语法,但函数本身就表现出闭包的行为。

TypeScript Code

function makeIncrementer(amount: number): () => number {
    let runningTotal = 0;
    return function(): number {
        runningTotal += amount;
        return runningTotal;
    }
}

const incrementByTen = makeIncrementer(10);
console.log(incrementByTen()); // Output: 10
console.log(incrementByTen()); // Output: 20

尾随闭包

尾随闭包(Trailing Closures)在 Swift 中是一个非常有用的特性,尤其是当闭包作为函数的最后一个参数时。尾随闭包语法使得在函数调用时将闭包写在函数括号之外,使得代码更加清晰。

let result = someFunction(arg1, arg2) { parameter in
    // 闭包体
}

Swift Code

func applyOperation(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

// Calling a function using a trailing closure
let result = applyOperation(2, 3) { $0 + $1 }
print("Result: \(result)") 

// Output: Result: 5

在以上示例中,applyOperation 函数接受两个整数和一个闭包作为参数。使用尾随闭包语法,我们可以将闭包写在函数调用的括号之外,使得代码更加简洁。在 TypeScript 中,并没有直接的尾随闭包语法,但可以通过将函数的最后一个参数定义为函数类型来达到类似的效果。

TypeScript Code

function applyOperation(a: number, b: number, operation: (a: number, b: number) => number): number {
    return operation(a, b);
}

const result: number = applyOperation(2, 3, (a, b) => a + b);
console.log(`Result: ${result}`);

// Output: "Result: 5"

逃逸闭包

逃逸闭包(Escaping Closures)是一个重要的概念,特别是在处理异步操作和回调时。逃逸闭包与非逃逸闭包的主要区别在于它们的生命周期:逃逸闭包可以在函数返回之后被调用,而非逃逸闭包则必须在函数返回之前被调用。逃逸闭包是在函数执行完毕后才被调用的闭包。在参数列表前加上 @escaping 关键字表示该闭包逃逸。

Swift Code

import Foundation

func loadData(completionHandler: @escaping (String) -> Void) {
    // 模拟异步操作
    DispatchQueue.global().async {
        let data = "Hello, Escaping closures!"
        DispatchQueue.main.async {
            completionHandler(data)
        }
    }
}

loadData { data in
    print("Received: \(data)")
}

// Output: Received: Hello, Escaping closures!

在以上示例中,completionHandler 是一个逃逸闭包,因为它在函数返回之后的某个时间点被调用。在 TypeScript 中,虽然并没有专门的逃逸闭包的语法,但我们可以通过传递函数作为参数,并在异步操作完成后调用该函数来模拟逃逸闭包的效果。

TypeScript Code

function loadData(): Promise {
    return new Promise((resolve) => {
        // 模拟异步操作
        setTimeout(() => {
            const data = "Hello, Closures!";
            resolve(data);
        }, 1000);
    });
}

loadData().then(data => {
    console.log(`Received: ${data}`);
});

// Output: "Received: Hello, Closures!"

Swift 的逃逸闭包和 TypeScript 的异步操作处理都是处理延时执行、异步执行的强大工具。它们都允许函数在完成某些操作后再执行一些代码,这在处理网络请求、数据库操作等异步任务时非常有用。

本文我们介绍了 Swift 中闭包表达式、尾随闭包和逃逸闭包等相关的内容。通过与 TypeScript 语法的对比,希望能帮助您更好地理解 Swift 的相关特性。

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...