Swift闭包的三种形式以及理解闭包的嵌套函数
Swift闭包的三种形式以及理解闭包的嵌套函数

Swift闭包的三种形式以及理解闭包的嵌套函数

闭包的定义

闭包(Closure) 是一种能够捕获和存储其所在上下文中的变量和常量的独立代码块。换句话说,闭包可以在定义时捕获其作用域内的变量,并在闭包的生命周期内使用和修改这些变量。

闭包在 Swift 中的表现有点像匿名函数,但更强大。它们可以作为参数传递,也可以作为返回值返回,还可以持有并修改其作用域内的变量,即使作用域已经销毁,闭包仍然可以访问这些变量。

闭包的三种形式

在 Swift 中,闭包有三种主要的形式:

1、全局函数:有名字并且不会捕获任何值的闭包。

全局函数是定义在全局范围内的函数,它们有名字,并且不会捕获任何值。这些函数可以在代码的任何地方使用,因为它们存在于全局作用域中。

func greet(name: String) -> String {
    return "Hello, \(name)!"
}

这里的 greet 就是一个全局函数。它不会捕获任何外部变量,因为它定义在全局范围内。

上面的全局函数在语法上是普通的命名函数,但实际上,也属于闭包的一种形式。

广义上来讲,闭包是一个功能块,可以被传递和使用。根据Swift文档的定义,闭包是一个可以捕获和存储外部作用域中的变量的字包含代码块。

2、嵌套函数:有名字并且可以捕获其上层函数中的值。

嵌套函数是定义在另一个函数内部的函数。它们可以捕获并存储外层函数中的变量。这使得嵌套函数在保持状态信息或创建一些专用的逻辑时非常有用。

func makeMultiplier(by multiplier: Int) -> (Int) -> Int {
    func multiplierFunction(number: Int) -> Int {
        return number * multiplier
    }
    return multiplierFunction
}

let multiplyByTwo = makeMultiplier(by: 2)
print(multiplyByTwo(3))  // 输出: 6

在这个例子中,multiplierFunction 是 makeMultiplier(by:) 的嵌套函数。由于 multiplierFunction 是在 makeMultiplier 内部定义的,它可以捕获 multiplier 的值并在执行时使用它。

3、闭包表达式:没有名字的闭包,可以捕获其上下文中的值。

闭包表达式是一段没有名字的代码块,可以捕获其所在上下文中的变量。它们是一种更灵活的匿名函数,可以用简洁的语法进行定义。闭包表达式非常适合用于需要传递函数作为参数的场景。

let add: (Int, Int) -> Int = { (a, b) in
    return a + b
}
print(add(3, 4))  // 输出: 7

在这个例子中,add 是一个变量,类型是 (Int, Int) -> Int,表示一个接受两个 Int 参数并返回一个 Int 值的闭包。

{ (a, b) in return a + b } 是一个匿名闭包,可以直接赋值给 add 变量。

(a, b) 是闭包的参数列表,-> Int 表示闭包的返回类型,in 之后是闭包的代码体。

实际上,上面的闭包表达式等价于:

func add(a: Int, b: Int) -> Int {
    return a + b
}
print(add(3, 4))  // 输出: 7

另外,闭包表达式的参数括号是可选的:

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

这里,a 和 b 是参数,直接用逗号分隔。

Swift 允许省略参数的括号,直接写成 a, b in,当参数数量明确且类型已指定时,编译器可以正确解析参数类型。

加括号 () 和不加括号对代码的功能没有影响,都是正确的闭包表达方式。在参数简单、数量少时可以省略括号,复杂情况时建议保留括号,以增加可读性。

闭包表达式和普通函数的区别

1、语法简洁度

闭包表达式:闭包更为灵活简洁,允许使用尾随闭包、省略参数名等语法糖,使其在某些上下文中更简洁和易于使用。例如,{ a, b in a + b } 就是完全简化后的闭包形式。

普通函数:普通函数的声明相对较正式,需要明确的函数名称、参数名和类型。

2、命名要求

闭包表达式:可以是匿名的。闭包通常在局部范围内定义并立即使用,因此不需要名称(可以将其直接赋给变量来调用)。

let numbers = [3, 1, 4, 2]
let sortedNumbers = numbers.sorted { $0 < $1 } // 直接传入闭包

这里的闭包 { $0 < $1 } 只用于 sorted 函数,不需要给闭包命名。闭包定义在这个局部范围内,任务完成后不再使用。

普通函数:必须有名称,适合在多处调用的情景。

3、捕获上下文(环境)

闭包表达式:闭包可以捕获其所在上下文中的变量值。比如,当闭包在函数中定义时,它可以访问函数作用域中的变量。

普通函数:普通函数不具备捕获能力,仅能访问其自身作用域内或通过参数传递的变量。

4、使用场景

闭包表达式:灵活、简洁、易于传递。用于异步操作、事件处理、数组排序等简短且不复杂的代码逻辑中。

let sortedArray = [5, 2, 3, 1].sorted { $0 < $1 }  // 使用闭包进行排序

普通函数:当需要编写复杂的逻辑且函数可能在多个地方使用时,普通函数更适合。

func sortArray(array: [Int]) -> [Int] {
    return array.sorted { $0 < $1 }
}
let sortedArray = sortArray(array: [5, 2, 3, 1])

为什么看起来像赋值?

因为 Swift 中,闭包本质上也是一种数据类型,所以可以像赋值其他数据类型(如 Int、String)一样进行赋值。

定义了闭包类型的变量后,你就可以把符合这个类型的闭包赋值给它。

变量后面为什么可以跟两个参数

这就是闭包的方便之处。由于 add 被赋值为一个接受两个参数的闭包,所以当你调用 add(3, 4) 时,它就像调用一个普通的函数一样,执行闭包里的代码。

在本例中,调用 add(3, 4) 相当于执行 { (a, b) in return a + b },其中 a = 3,b = 4,结果返回 7。

闭包的语法

闭包表达式的语法一般如下:

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

参数列表:和函数参数类似,用来传递输入值给闭包。

返回类型:定义了闭包返回的值。

in:分隔了参数和闭包体。

闭包的特点

捕获环境变量:闭包可以捕获其定义时的上下文变量(外部变量),并且在闭包的生命周期内这些变量都可以被访问和修改。

延迟执行:闭包不会在定义时执行,而是在你明确调用时执行。你可以将闭包作为参数传递给函数或者存储在变量中,随时调用。

可以作为返回值:闭包可以从一个函数中返回,并且可以在其他地方使用。

理解闭包的嵌套函数

代码分析

前面涉及闭包的嵌套函数部分,在这里进一步进行解析:

func makeMultiplier(by multiplier: Int) -> (Int) -> Int {
    func multiplierFunction(number: Int) -> Int {
        return number * multiplier
    }
    return multiplierFunction
}
函数makeMultiplier(by:)

首先是一个makeMultiplier(by:)函数,它接受一个整数multiplier作为参数,返回值的类型是(Int) -> Int,表示返回的函数会接受一个Int并且返回一个Int。

嵌套函数multiplierFunction(number:)

里面是嵌套函数multiplierFunction(number:),它接受一个参数number,并且返回number * multiplier。

返回值

最后,makeMultiplier(by:) 返回的是 multiplierFunction,这是一个可以使用 multiplier 参数的函数。

使用过程

let multiplyByTwo = makeMultiplier(by: 2)
print(multiplyByTwo(3))  // 输出: 6
makeMultiplier(by: 2) 调用

当调用 makeMultiplier(by: 2) 时,multiplier 被设置为 2。

它返回一个嵌套函数 multiplierFunction,这个函数会将 multiplier 捕获。

现在,multiplyByTwo 变量保存了 multiplierFunction,并且记住了 multiplier 的值 2。

multiplyByTwo(3) 调用

调用 multiplyByTwo(3) 相当于调用 multiplierFunction(number: 3)。

multiplierFunction 使用了捕获的 multiplier 值(2),计算 3 * 2,最终结果为 6。

嵌套函数的特点:嵌套函数可以访问外部函数的参数和变量。在 makeMultiplier(by:) 中,multiplierFunction 就能使用外部的 multiplier 参数,这样的机制称为“捕获”。

返回函数:makeMultiplier(by:) 返回了 multiplierFunction,这个函数在调用时仍然“记得”它的 multiplier 参数。

闭包的常见用途

回调函数:在异步操作中,当操作完成时执行某个代码块。

事件处理器:处理按钮点击、手势等用户操作。

作为参数传递和返回:提高代码的灵活性和可复用性。

内存管理:捕获外部变量来维持状态信息。

总结来说,闭包是一种灵活且强大的代码块,允许你将代码的逻辑和状态保持在一起,方便传递和操作。这使得 Swift 开发中的异步操作、回调和数据处理变得更加自然和高效。

闭包是否会被自动释放

闭包是否会被自动释放,取决于是否有其他强引用指向它:

局部闭包:在局部范围定义、使用的闭包,通常不会被外部引用,因此任务完成后会自动释放。比如上面例子中的 { $0 < $1 },只在 sorted 函数中使用完毕后会被释放。

持有外部引用的闭包:如果闭包被传递到其他地方,比如被赋值给一个属性、保存在数组中,或被长期引用,那么它将不会立即释放,直到它的所有强引用都被解除。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注