swift之闭包(closure)
最后更新于:2022-04-01 11:39:49
闭包是⾃包含的函数代码块,可以在代码中被传递和使⽤。 Swift 中的闭包与 C 和Objective-C 中的代码块(blocks)以及其他⼀些编程语⾔中的 lambdas 函数⽐较相似。
闭包可以捕获和存储其所在上下⽂中任意常量和变量的引⽤。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
在函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之⼀:
全局函数是⼀个有名字但不会捕获任何值的闭包
嵌套函数是⼀个有名字并可以捕获其封闭函数域内值的闭包
闭包表达式是⼀个利⽤轻量级语法所写的可以捕获其上下⽂中变量或常量值的匿名闭包
Swift 的闭包表达式拥有简洁的⻛格,并⿎励在常⻅场景中进⾏语法优化,主要优化如下:
利⽤上下⽂推断参数和返回值类型
隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字
参数名称缩写
尾随(Trailing)闭包语法
1、闭包表达式
(1)先看一下自带的sort函数
Swift 标准库提供了名为 sort 的函数,会根据您提供的⽤于排序的闭包函数将已知类型数组中的值进⾏排序。 ⼀旦排序完成, sort(_:) ⽅法会返回⼀个与原数组⼤⼩相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sort(_:) ⽅法修改。
~~~
let strs = ["ashdif", "gsdfao", "dsdokfn", "csddf"]
func compare(str1 : String, str2 : String) -> Bool {
return str1 > str2
}
var newStrs = strs.sort(compare)
print(strs)
print(newStrs)
//["ashdif", "gsdfao", "dsdokfn", "csddf"]
//["gsdfao", "dsdokfn", "csddf", "ashdif"]
~~~
(2)闭包表达式语法
闭包表达式语法有如下⼀般形式:
~~~
{ (parameters) -> returnType in
statements
}
~~~
闭包表达式语法可以使⽤常量、变量和 inout 类型作为参数,不提供默认值。 也可以在参数列表的最后使⽤可变参数。 元组也可以作为参数和返回值。
~~~
let strs = ["ashdif", "gsdfao", "dsdokfn", "csddf"]
func compare(str1 : String, str2 : String) -> Bool {
return str1 > str2
}
var newStrs = strs.sort(compare)
print(strs)
print(newStrs)
var newStrs1 = strs.sort({(str1 : String, str2 : String) -> Bool in
return str1 < str2
})
print(newStrs1)
//["ashdif", "gsdfao", "dsdokfn", "csddf"]
//["gsdfao", "dsdokfn", "csddf", "ashdif"]
//["ashdif", "csddf", "dsdokfn", "gsdfao"]
~~~
(3)根据上下文推断类型
~~~
let strs = ["ashdif", "gsdfao", "dsdokfn", "csddf"]
func compare(str1 : String, str2 : String) -> Bool {
return str1 > str2
}
var newStrs = strs.sort(compare)
print(strs)
print(newStrs)
var newStrs1 = strs.sort({(str1 : String, str2 : String) -> Bool in
return str1 < str2
})
print(newStrs1)
var newStrs2 = strs.sort({str1, str2 in return str1 > str2})
print(newStrs2)
//["ashdif", "gsdfao", "dsdokfn", "csddf"]
//["gsdfao", "dsdokfn", "csddf", "ashdif"]
//["ashdif", "csddf", "dsdokfn", "gsdfao"]
//["gsdfao", "dsdokfn", "csddf", "ashdif"]
~~~
(4)单表达式闭包隐式返回
单⾏表达式闭包可以通过隐藏 return 关键字来隐式返回单⾏表达式的结果
~~~
var newStrs2 = strs.sort({str1, str2 in str1 > str2})
~~~
(5)参数名称缩写
Swift ⾃动为内联函数提供了参数名称缩写功能,您可以直接通过 $0 , $1 , $2 来顺序调⽤闭包的参数。
如果您在闭包表达式中使⽤参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进⾏推断。 in 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
~~~
var newStrs2 = strs.sort({ $0 > $1})
~~~
(6)运算符函数
实际上还有⼀种更简短的⽅式来撰写上⾯例⼦中的闭包表达式。 Swift 的 String 类型定义了关于⼤于号 ( > ) 的字符串实现,其作为⼀个函数接受两个 String 类型的参数并返回 Bool 类型的值。
~~~
var newStrs2 = strs.sort(>)
~~~
2、尾随闭包
如果您需要将⼀个很⻓的闭包表达式作为最后⼀个参数传递给函数,可以使⽤尾随闭包来增强函数的可读性。 尾随闭包是⼀个书写在函数括号之后的闭包表达式,函数⽀持将其作为最后⼀个参数调⽤。
~~~
var newStrs3 = strs.sort() {$0 > $1}
~~~
~~~
let hanzi = [0 : "零", 1 : "壹", 2 : "贰", 3 : "叁", 4 : "肆", 5 : "伍", 6 : "陆", 7 : "柒", 8 : "捌", 9 : "玖"]
let numbers = [23342, 832, 976]
let numbersHZ = numbers.map() {
(var number) -> String in
var outHZ = ""
while number > 0 {
outHZ = hanzi[number % 10]! + outHZ
number /= 10
}
return outHZ
}
print(numbers)
print(numbersHZ)
//[23342, 832, 976]
//["贰叁叁肆贰", "捌叁贰", "玖柒陆"]
~~~
再来一个简单的例子:
~~~
func trailingClosure(a : Int, b : Int, c : (Int, Int) -> Bool) -> Int{
if c(a, b) {
return a
}else {
return b
}
}
let result = trailingClosure(10, b: 13) {//我的理解,简单来说就是写在括号外面,看着好看
$0 > $1
}
print(result)
//13
~~~
3、捕获值
闭包可以在其定义的上下⽂中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引⽤和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
~~~
//求一个数x的y次方
func makecalculate(x : Int, y : Int) -> () -> Int {
var ret = 1
func achieve() -> Int {
for var i = 0; i < y; i++ { //捕获参数y
ret *= x //捕获变量ret和参数y
}
return ret
}
return achieve
}
let achiveFunc = makecalculate(3, y: 4)
print(achiveFunc())
//81
~~~
achieve函数并没有任何参数,但是在函数体内访问了 ret 和 x , y 变量。这是因为其通过捕获在包含它的函数体内已经存在的 ret 和 x , y 变量的引⽤(reference)⽽实现。捕捉了变量引⽤,保证了 ret 和 x, y 变量在调⽤完 makecalculate 后不会消失,并且保证了在下⼀次执⾏ achieve 函数时, ret 可以继续增加。
4、闭包是引用类型
⽆论您将函数/闭包赋值给⼀个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引⽤