Swift编程语言中闭包(closure)的使用

Swift里的闭包(Closure)是一个程序块。该程序块可以在Swift表达式或函数中被调用和输入输出。它是一种匿名函数,也有和函数一样的输入和输出,和Python中的lambda函数比较类似(见[1]),但也有区别:Python中的lambda函数只允许有一个表达式[1],但Swift里的闭包里面可以有多行表达式。

一、闭包的三种定义方式

这里以一个简单的判断两个数值大小的闭包为例

1. 多行函数的形式

//This function is used to compare two elements, 
//returns whether the left is smaller than right
let compLSRB:(Int,Int) -> Bool = {

    (a1:Int, a2:Int) -> Bool in

    return a1 < a2

}

对该段代码的解读如下

注意:就像普通的常量/变量一样,闭包的类型其实可以省略,因为等号后的内容,即闭包的定义已经可以确定闭包常量/变量的类型了。

但若只输入

var compLSRB: (Int, Int) -> Bool

即对闭包只作出声明,在之后几行代码才做定义(赋值),则闭包类型不可省略!

2. 简易表达式的形式

let compLSRB2:(Int,Int)->Bool = {(a1, a2) in a1 < a2}

对该代码的解读如下

这里只需一行表达式。

注意此处闭包类型不可省略,因为右侧的简易闭包表达式未能确定闭包的参数和输出类型!

3. 极简形式

let compLSRB3:(Int,Int)->Bool = {$0 < $1}

对该代码解读如下

这里更为简易,不需要输入参数名。

注意此处闭包类型不可省略,因为右侧的简易闭包表达式未能确定闭包的参数和输出类型!

二、闭包被其它函数调用的方式

在Swift里,闭包也是一个正常的变量/常量。一些函数会以闭包作为输入参数,故将闭包输入函数的方式同将其它变量/常量输入函数的方式大同小异。

1. 用一般方式将闭包输入函数

这里使用一个列表的sorted函数,该函数用输入的闭包作为排序列表的依据。函数的具体说明见[2],较简单的介绍见[3]。大体上,sorted函数的输入参数"by"是一个闭包。

可以把代表该闭包的常量/变量名输入函数

var ListToSort:[Int] = [4,2,5,5]
var ListSorted = ListToSort.sorted(by: compLSRB)
//From small to large, ascending sort
print("Sorted [4,2,5,5] is \(ListSorted)")

这里输入的compLSRB即本文开头定义的闭包

也可以把闭包的带"{}"的定义直接输入函数

var ListSorted1_5 = ListToSort.sorted(by: {
    (a1:Int, a2:Int) -> Bool in
        return a1 < a2
})

注意:输入的闭包可以多行

​​​​​​​或者用简易的闭包定义

var ListSorted2 = ListToSort.sorted(by: {(a1, a2) in a1 < a2})

注意:此处的闭包无需声明类型,因为.sorted函数已经可以确保输入的闭包的类型了。

2. 用尾随(trailing)方式将闭包输入函数

只有一个闭包输入参数

如果一个函数只有一个闭包输入参数,无论是否还有其它的非闭包输入参数,则该函数可使用尾随(trailing)的方式输入闭包,即直接将闭包放置于函数尾端,无需将闭包放置在函数的"()"内[4]。

var ListSorted5 = ListToSort.sorted{$0 < $1}

此处,在闭包{$0 < $1}外,没有"()"。闭包尾随调用它的函数。

有多个闭包输入参数

如果一个函数有超过一个闭包输入参数,那么第一个闭包参数仍然按照只有一个闭包输入参数的尾随方式,而其余闭包则尾随上一个闭包,尾随方式是外部参数名:闭包。

注:Swift函数的外部参数名的概念请参照[5]。

func twoClosures(cl1:(Int)->Bool, cl2:(Int)->Bool){
    print(cl1(1) && cl2(2))
}
twoClosures{$0<0}cl2:{$0>0}

此处,最后一行的函数twoClosures调用闭包时,第二个闭包输入前有"cl2:"。其中"cl2"是外部参数名。

如果函数的参数的外部参数名是下划线"_",表示参数被忽略[6]。此时,函数调用闭包时,第二个闭包前需加"_:"。

func twoClosures(_ cl1:(Int)->Bool,_ cl2:(Int)->Bool){
    print(cl1(1) && cl2(2))
}
twoClosures{$0<0}_:{$0>0}

三、闭包的捕获(Capture)数值的功能

当一个闭包是函数返回的常量/变量时,如果这个闭包的代码涉及了函数里的闭包以外的变量,那么即使函数已经运行完毕,这个闭包仍然会捕获(Capture)该变量的数值[7]。在该闭包脱离函数运行的过程中,它所捕获的变量数值仍然有效,也仍然可以对该变量进行修改,并利用修改后的数值。

import Foundation

func createAdder() -> ((Int?) -> Int){
    //Create a closure the input of which is an optional int.
    //with int input, add to amount.
    //with nil, return to 0
    var currAmount = 0
    var adding = {(_ addition:Int?) -> Int in
        if (addition != nil){
            currAmount += addition!
        }
        else{
            currAmount = 0
        }
        return currAmount
    }
    return adding
}

var adder = createAdder()
var step1 = adder(2)
print("After running adder(2), the result is \(step1)")
var step2 = adder(-3)
print("After running adder(-3), the result is \(step2)")
var step3 = adder(0)
print("After running adder(0), the result is \(step3)")
var step4 = adder(nil)
print("After running adder(nil), the result is \(step4)")

var step5 = adder(-1)
print("After running adder(-1), the result is \(step5)")
//Now create another createAdder closure
var adder2 = createAdder()
var step6 = adder2(5)
print("After running the new adder adder2(5), the result is \(step6)")

运行的结果是:

对该代码解读如下

四、总结

闭包(Closure)是一种函数,但它也是一个常量/变量,可以以更简易的方式定义。可以作为参数输入函数,而以尾随(Trailing)方式写出的代码更易读。与此同时,闭包还有捕获(Capture)变量数值的功能。

参考资料

[1]​​​​​​​​​​​​https://www.runoob.com/python3/python-lambda.html

[2]https://developer.apple.com/documentation/swift/array/sorted(by:)

[3]https://www.jianshu.com/p/013a1d82cad5

[4]https://www.codingexplorations.com/blog/swift-functions-and-closures-a-comprehensive-guide

[5]https://blog.csdn.net/beyondforme/article/details/106355526

[6]https://blog.csdn.net/huxinguang_ios/article/details/79422265

[7]https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值