一、闭包表达式
在Swift中,可以通过func定义一个函数。也可以通过闭包表达式定义一个函数
func test(v1:Int,v2:Int) ->Int {
return v1 + v2;
}
-----------------------------------------------------------------------
let fn = {
(v1:Int,v2:Int) ->Int in
return v1 + v2;
}
print(fn(10,20));
- 闭包表达式语法基本形式
{ (parameters) -> (return type) in
statements
}
闭包的函数整体部分由关键字 in 导入,这个关键字表示闭包的形式参数类型和返回类型定义已经完成,并且闭包的函数体即将开始。
- 定义一个闭包表达式
let fn = {
(v1:Int,v2:Int) ->Int in
return v1 + v2;
}
print(fn(10,20));
import UIKit
class Student{
func test(){
{
(v1:Int,v2:Int) ->Int in
print("闭包表达式")
return v1 + v2;
}(10,20)
}
}
let s = Student()
s.test()
- 闭包表达式的类型
闭包表达式本质是函数,其类型和函数的类型一样。可分为: () -> (),(Int) -> (),(Int) -> (Int),() -> (Int) ······等
二、闭包表达式作为参数传递
- (void)execWithFn:( NSInteger (^)(NSInteger, NSInteger))fn{
fn(10,20);
}
oc中block可以作为函数的参数进行传递,swift中闭包当然也可以作为函数的参数进行传递
class Student{
func exec(fn: (Int,Int) -> Int) {
print(fn(1,2))
}
}
let s = Student()
s.exec (fn: { (v1:Int, V2:Int) -> Int in
print("闭包")
return v1 + V2
})
class Student{
func exec(a:Int,b:Int,fn: (Int,Int) -> Int,c:Int) {
print(fn(1,2))
}
}
let s = Student()
s.exec (a:10,b:20,fn: { (v1:Int, V2:Int) -> Int in
print("闭包")
return v1 + V2
},c:21)
三、尾随闭包
如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式。
class Student{
func exec(a:Int,b:Int,fn: (Int,Int) -> Int) {
print(fn(1,2))
}
}
let s = Student()
//未使用尾随闭包
s.exec (a:10,b:20,fn: { (v1:Int, V2:Int) -> Int in
print("未使用尾随闭包")
return v1 + V2
})
//使用了尾随闭包
s.exec (a: 10, b: 20){ (c:Int, d:Int) -> Int in
print("使用了尾随闭包")
return c + d
}
- 如果闭包是函数的最后一个参数,则可以将闭包写在()后面,这种写法就是尾随闭包。
class Student{
func exec(fn:(Int,Int) -> Int) {
print(fn(1,2))
}
}
let s = Student()
s.exec { (c:Int, d:Int) -> Int in
return c + d
}
- 如果闭包表达式是函数的唯一实参,而且使用了尾随的语法,那就可以省略函数名后的圆括号。
四、自动闭包
官方解释:自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。
func test(wage:Int,fn:()->(Int)){
if wage<5000 {
print("不用交税")
}else{
print("\(fn())元要交税")
}
}
var wage = 8000
test(wage: wage){
return (wage-5000)
}
wage = 2000
test(wage: wage){
return (wage-5000)
}
以上代码的意思是,当wage = 8000,需要交wage-5000的税,而wage =2000,不需要交税。不需要交税我们就不必去计算wage-5000。我们设计 test函数中计算税的表达式为一个闭包参数,这样就可以在需要的时候执行,提高了效率。
以上是正常的闭包定义和调用,如果我们使用了自动闭包
func test(wage:Int,fn:@autoclosure()->(Int)){
if wage<5000 {
print("不用交税")
}else{
print("\(fn())元要交税")
}
}
var wage = 8000
test(wage: wage,fn: wage-5000)
wage = 2000
test(wage:wage, fn:wage-5000)
wag-5000是一个整数运算表达式,却能传递给类型为闭包的参数fn,很惊喜吧。这是@autoclosure标记后,test(wage: wage,fn: wage-5000)虽然fn表面上看传递的是wage-5000,编译器会自动把传递的表达式加上{},从而转换为闭包{wage-5000}。也就是最终的调用是test(wage: wage,fn: {wage-5000})。这就是自动闭包吧。我们这时再去看官方的解释,也许就会理解它所表达的意思。
- @autoclosure 会自动将 20 封装成闭包 { 20 }
- @autoclosure 只支持 () -> T 格式的参数
- @autoclosure 并非只支持最后1个参数
- 空合并运算符 ?? 使用了 @autoclosure 技术
- 有@autoclosure、无@autoclosure,构成了函数重载
五、逃逸闭包
逃逸闭包、非逃逸闭包,一般都是当作参数传递。
1、非逃逸闭包
func test(_ fn:(Int,Int) -> Int){
fn(1,2)
}
test ({ (v1:Int, v2:Int) -> Int in
v1 + v2
})
闭包fn的调用是在test函数作用域内,发生在 函数结束前。这就是非逃逸闭包
2、逃逸闭包
import UIKit
class Person {
var testFn:(Int,Int) ->Int
init(fn:@escaping (Int,Int)->Int) {
self.testFn = fn
}
}
var p = Person(fn:
{
(v1:Int,v2:Int) in
v1 + v2
}
)
print(p.testFn(1,2))
闭包fn的调用不是直接在函数内调用,而是在init函数外调用,闭包调用了逃离了函数的作用域,这就是逃逸闭包。需要用@escapings声明
- 非逃逸闭包:闭包的调用发生在函数结束前,闭包调用在函数作用域内
-逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域
六、闭包
一个函数和它所捕获 的变量\常量组合起来,称之为 闭包