版权声明:不积跬步无以至千里,不积小流无以成江海! https://blog.csdn.net/Bolted_snail/article/details/87607679
文章目录
闭包的定义
- 一门语言要支持闭包有两个前提:
- 支持函数类型,能够将函数作为参数或返回值传递
- 支持函数嵌套。
- 这两个前提Swift都满足,Swift中闭包的定义:闭包是自包含的匿名函数代码块,可以作为表达式,函数参数,函数返回值,闭包表达式的运算本质是一种函数类型。
- 闭包表达式标准语法格式
{(参数列表) -> 返回值类型 in
语句组
}
func calculate(opr:String) -> (Int,Int) -> Int {
var result: (Int,Int)->Int
switch opr {
case "+":
result = {
(a:Int,b:Int) -> Int in
return a + b
}
case "-":
result = {
(a:Int, b:Int) -> Int in
return a - b
}
default:
result = {
(a:Int,b:Int) -> Int in
return a + b
}
}
return result
}
let addClosure = calculate(opr: "+")
print("6+6 = \(addClosure(6, 6))")
let subClosure = calculate(opr: "-")
print("6-6 = \(subClosure(6,6))")
addClosure
常量其实是calculate
里面加法计算的闭包,subClosure
是里面的加法计算的闭包,在调用是参数没有标签,因为addClosure
返回的函数类型只有参数和返回值类型(也不允许添加参数标签),所以在闭包调用表时参数不带提示,所以闭包是自包含的匿名代码块,闭包本质上函数类型。
闭包简化
- 上面示例是闭包的标准形式
{
(a:Int,b:Int) -> Int in
return a + b
}
- 类型推断简化,Swift能推断参数a,b都是Int类型,返回值也是Int乐行,所以可以去掉类型,简化如下:
{(a,b) in return a + b }
参数列表括号也可以省略:
{a,b in return a + b }
- 隐藏return关键字:如果闭包内部语句组只有一条语句,如
return a + b
,那么这种语句都是返回语句,前面的关键字 return可以省略:
{a, b in a + b}
省略的前提是闭包中只有一条return语句。
- 省略参数名:Swift提供了参数名省略功能,我们可以用$0、$1、$2…来指定闭包中的参数。$0指代第一个参数, n指代第n+1个参数。
func calculate2(opr:String) -> (Int,Int) -> Int {
var result: (Int,Int)->Int
switch opr {
case "+":
result = {$0+$1}
default:
result = {$0-$1}
}
return result
}
let addf = calculate(opr: "+")
print(addf(9,9)) // 18
闭包使用
- 使用闭包返回值(使用闭包定义一个常量或变量): 闭包的本质是函数类型,是有返回值的,我们可以直接在表达式中使用闭包的返回值
let f1: (Int,Int)->Int = {$0 + $1}
print(f1)
let v1 = f1(1,2)
print(v1)
let v2 = {$0 + $1}(1,2)
print(v2)
f1常量是一个参数我连个Int 类型,返回值为两个参数和的闭包,是一个函数,v1是这个闭包被传参调用,v2是在定义了闭包的痛是给闭包传参调用。所以f1是闭包类型(函数类型),v1,v2是Int类型。
- 闭包作为函数参数:
//加法闭包
let f1: (Int,Int)->Int = {$0 + $1}
//加法闭包
let f2: (Int,Int)->Int = {$0 - $1}
func calculate3(funcName:(Int,Int)->Int,a:Int,b:Int) ->Int{
return funcName(a,b)
}
//闭包作为函数参数调用
//加法
print("3+2=\(calculate3(funcName: f1, a: 3, b: 2))") //3+2=5
//减法
print("3-2=\(calculate3(funcName: f2, a: 3, b: 2))") // 3-2=1
闭包f1,f2做参数传给函数调用。
- 闭包作为函数返回值:
func calculate2(opr:String) -> (Int,Int) -> Int {
var result: (Int,Int)->Int
switch opr {
case "+":
result = {$0+$1}
default:
result = {$0-$1}
}
return result
}
let addf = calculate(opr: "+")
print(addf(9,9)) // 18
尾随闭包
- 尾随闭包就是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
func calculate4(a:Int,b:Int,funcName:(Int,Int)->Int) ->Int{
return funcName(a,b)
}
//一般简写写法
let va1 = calculate4(a: 5, b: 6, funcName: {$0+$1})
//尾随闭包简写写法
let va2 = calculate4(a: 5, b: 6){$0+$1}
print(va1,va2)
注意尾随闭包,必须是闭包作为函数的最后一个参数,不是最后一个参数不能用尾随闭包写法。
闭包捕获上下文中的变量和常量
- 嵌套函数或闭包可以访问它所在上下文的变量和常量,这个过程称为
捕获值
,即使是定义这些常量和变量的原始作用域已经不存在了,嵌套函数或闭包仍然可以在函数或闭包体内引用和修改这些值。
//捕获上下文变量和常量
func makeArr() -> (String) ->Array<String> {
var arr:[String] = [String]()
return{
(item:String) ->[String] in
arr.append(item)
return arr
}
}
let func1 = makeArr()
print(func1("张三"))
print(func1("李四"))
print(func1("王五"))
let func2 = makeArr()
print(func2("张3"))
print(func2("李4"))
print(func2("王5"))
print(func2("宋6"))
理论上在makeArr
调用完后,里面的变量arr做作用域已经没了,如果没给其他变量赋值,arr就销毁了。但从打印结果来看arr并没有销毁。这是因为arr变量相对于闭包而言,是一个上下文变量,因为arr在闭包中被使用和返回,所以闭包每次调用的时候,表里arr都能够被保持。func1,func2是makeArr
被调用时返回的闭包,makeArr
就相应的创建了两个可变数组arr,这个两个可变数组的真实作用域跟func1,func2是一致的。
- 判断是闭包还是函数:闭包中往往有一些关键字,如 in,还有缩写参数$0,$1等等,这些特征函数是没有的。