自包含(什么意思😂)的函数代码块
特点:捕获和存储上下文的任意常量和变量的引用
有三种闭包:
- 全局函数:有名字,不捕获 (闭包性体现在哪里呢)
- 嵌套函数:有名字,捕获封闭函数域内值
- 闭包表达式:捕获上下文值得匿名闭包
Swift的闭包主张简洁,优化:
- 利用上下文进行类型推断
- 隐式返回单表达式闭包,闭包体只有单个表达式,可以省略
return,返回值为表达式的值 - 参数名称缩写
- 尾闭包
闭包表达式
是一种利用简洁语法构建内联闭包的方式,语法为:
{ (parameters) -> returnType in
statements
}
closure不能带标签(外部参数名),但是可以用_忽略(社么鬼?)
var closure = { (_ a:Int, b:Int) in
print(a,b)
}
closure(1,2)
参数可以为in-out参数,但不能设定默认值,可以使用可变参数(必须放最后一位),假如不放最后一位呢?
var closure = { (a:Int..., b:Int) in
print(a,b)
}
closure(11,11,22)//闭包无法调用,在定义closure时就该报错了
具体可以看http://stackoverflow.com/questions/39548852/swift-3-0-closure-expression-what-if-the-variadic-parameters-not-at-the-last-pl (没体会到意思😂,感觉有点不对)
我的理解:
为什么?因为闭包没有标签名,编译器无法知道可变参数有多长
放在最后一位的话,别的参数分配完了后,剩下的都是可变长参数的了
sorted方法
sorted(by: (String, String) -> Bool);//sorted方法原型
sorted不会对原数组产生影响,会产生一个新数组,sorted函数参数接受一个闭包,全局函数就是闭包:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
以上是全局函数的方式传递闭包
使用闭包表达式
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
用in关键字来分隔参数和闭包体,这种方式也叫内联闭包
上下文类型推断
闭包参数类型:根据数组内元素类型推断
闭包返回值:sorted已经指出
所以闭包可以省略类型
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
其实,像这种内联闭包表达式作为函数参数,总是可以被推断类型,所以,总是可以省略
单表达式闭包隐式返回
闭包体中如果只有一个表达式,省略return,会返回这个表达式的值
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
参数名称缩写
我们不需要去定义s1和s2,可以使用名称缩写,$0,$1,$2按顺序表示参数表中的参数,所以参数表可以去掉,in也要去掉
reversedNames = names.sorted(by: { $0 > $1 } )
运算符方法
终极闭包
reversedNames = names.sorted(by: >)
尾闭包
一个书写在函数括号后的闭包表达式,遇见这种格式,函数会把闭包作为最后一个参数调用
使用条件:闭包表达式作为最后一个参数时,使用尾闭包增强可读性。
使用尾闭包时,不用写出函数的外部名
func abc(a:Int,closure:()->Void){
}
abc(a:1) {
} //使用尾闭包
abc(a:1,closure: {
})//不使用尾闭包
//当参数只有一个闭包表达式时,括号也可以省略
func abc(closure:()->Void){
}
abc{
}//使用尾闭包
对sorted使用尾闭包
reversedNames = names.sorted() { $0 > $1 }
reversedNames = names.sorted { $0 > $1 }
func abc(closure:(Int,String)->Void){
closure(1,"2")
}
abc { (tuple) in
print(tuple)//tuple是一个按照参数表来的元组
}
思考问题:🤔如何让闭包调用时带有外部参数名?
Swift中的参数貌似只能是let型,除非使用in-out,如果想使用var,需要把let类型的常量值给var类型的变量值
值捕获
闭包可以捕获被定义上下文中的常量或者变量。即使这些值的作用域已经不在,闭包依然可以使用这些值(延长了值的生命周期)
嵌套函数可以捕获外部函数所有的参数以及定义的常量和变量
func xyz() -> ()->Void {
var a = 1
func abc(){//捕获a的引用
a = a + 1
print(a)
}
a = 100 //会影响到abc内的值
abc()
return abc
}
var m = xyz()//101
m()//102
m()//103
abc捕获了a的引用,xyz执行完毕后,a还在,因为abc延长了a的生命周期
注意类的循环引用,以后再做讨论
闭包是引用类型
常量值类型,常量引用类型的区别:
- 常量值类型的一切都是常量,一旦确定不可更改
- 常量引用类型的引用不可以改变,但是引用指向的对象是可以改变的
变量值类型,变量引用类型:
- 都可以变
值类型和引用类型的本质:给值类型变量起个名字,这个名字就代表这个值,给引用类型变量起个名字,这个名字仅仅代表这个变量的引用,不能代表这个变量本身。还需要讨论
var a = 1//a是
var b = {
}//
值在传递时只能拷贝,引用在传递时可以只传递引用(其实是地址的拷贝)
逃逸闭包
闭包作为参数传递给函数,函数返回后,才执行这个闭包,我们称这个闭包为逃逸闭包。或者说作为函数参数的闭包可以在函数体外被调用叫做逃逸闭包。逃逸闭包必须指定@escaping
如何让闭包逃逸?把闭包保存在全局变量中,函数执行完毕后再调用闭包
var outcl:(()->Void)? //注意闭包类型的括号,没有括号就是返回值为Void?可选了
func taoyibibao(incl:@escaping ()->Void) {
outcl = incl;
}
taoyibibao {
}
outcl!()//调用必须被初始化,所以`taoyibibao`需要先执行
逃逸闭包必须显式调用self
var outTaoyiCl:(()->Void)?
func taoyi(inTaoyiCl:@escaping ()->Void) {
outTaoyiCl = inTaoyiCl
}
func butaoyi(inBuTaoyiCl:()->Void) {
inBuTaoyiCl()
}
class A {
var x:Int = 1
func doSomeThing() {
taoyi {
self.x = 2 //逃逸出去的闭包必须显式使用self来捕获当前类实例
}
butaoyi {
x = 2 //非逃逸闭包则可以省略self
}
}
}
自动闭包
使用条件:没有参数,返回闭包体中表达式的值
func abc(xyz:()->String) {
print(xyz())
}
abc {"呵呵"}
改为自动闭包
func abc(xyz:@autoclosure ()->String) {
print(xyz())
}
abc(xyz: "呵呵");//声明成自动闭包后,参数变为一个值(值或者返回值的表达式或者函数等),这个值会被自动转换成闭包
自动闭包就是调用形式变了,本来形参为闭包表达式,加了@autoclosure,就可以只传一个值或者表达式或者返回这种类型的函数调用。