/*闭包:类似block代码块,可以在代码中传递和使用
闭包的形式:
1.全局函数:有名字,不会捕获任何值
2.嵌套函数:有名字,能捕获其封闭函数域内值
3.闭包表达式:没名字,可以捕获其上下文中变量或常量值
闭包表达式语法的一般形式:
{(参数) -> 返回类型 in
参数不能设定默认值,可变参数(N个参数)需要放在参数列表的最后一位
处理逻辑
}
*/
//=======================闭包表达式 - 排序示例=======================
//闭包表达式:是一种利用 简洁语法 构建 内联闭包 的方式;示例如下:
let names = ["Chris","Alex","Ewa","Barry","Daniella","Lily","Swift"]
//调用函数的排序(参数和返回值在大括号外)
func backward(s1: String, s2: String) -> Bool {
return s1 > s2 //如果当前排序结束后第一个参数值出现在前面,返回true,否则返回false
}
var reversedNames = names.sorted(by: backward)//sorted(by:)方法接受一个闭包
print("调用函数的降序结果:\(reversedNames)")
//闭包排序1:内联闭包表达式(参数和返回值在大括号内)
let reversedNames1 = names.sorted(by:{ (s1: String, s2: String) -> Bool in //大括号内就是闭包的一般形式
return s1 > s2
})
print("闭包一般形式排序:\(reversedNames1)")
//闭包排序2:能推断出的类型都省略
let reversedNames2 = names.sorted(by: { s1, s2 in//参数类型和返回值类型根据上下文都能推断出来,所以可以省略
return s1 > s2
})
print("闭包省略类型排序:\(reversedNames2)")
//闭包排序3:单表达式闭包 可以省略return关键字 隐式返回
let reversedNames3 = names.sorted(by: {s1, s2 in
s1 > s2
})
print("闭包省略返回关键字排序:\(reversedNames3)")
//闭包排序4:参数名称缩写 in关键字省略
let reversedNames4 = names.sorted {$0 > $1}
print("闭包参数缩写的排序:\(reversedNames4)")
//闭包排序5:运算符方法(极简)
let reversedNames5 = names.sorted(by: >)
//String类型定义了关于>的字符串实现,所以可以简单的这么干
print("闭包运算符方法排序:\(reversedNames5)")
//=======================尾随闭包=======================
//尾随闭包:是一个写在函数括号之后,作为函数最后一个参数调用的闭包表达式,不需要写出闭包的参数标签
func someFuncThatTakesAClosure(closure:() -> Void){
print("函数体部分")
}
someFuncThatTakesAClosure(closure: {
print("闭包主体部分--不使用尾随闭包进行函数调用")
})
someFuncThatTakesAClosure() {
print("闭包主体部分--使用尾随闭包进行调用")
}
someFuncThatTakesAClosure {
print("如果闭包表达式是函数或方法的唯一参数,在使用尾随闭包时可以省略()")
}
//示例:将整型数组转换成对应的字符串数组
let numbers = [12,54,521]
let digitNames = [0:"Zero",1:"One",2:"Two",3:"Three",4:"Four",5:"Five",6:"Six",7:"Seven",8:"Eight",9:"Nine"]
let mapArr3 = numbers.map { (number) -> String in//不需要指定number的类型,因为可以从要映射的数组中判断出来
var number = number//闭包或者函数的参数总是常量,所以需要一个局部变量来控制循环
var output = ""
repeat {
output = digitNames[number % 10]! + output//取最后一位(字典下标返回的是一个可选值,所以需要!解包)
number /= 10//取除最后一位外的其他几位
}while number > 0//只要大于0就一直循环
return output//闭包表达式指定的返回类型是String,指明了新的映射数组类型为字符串数组
}
print("map的示例3:\(mapArr3)")//["OneTwo", "FiveFour", "FiveTwoOne"]
//=======================值捕获=======================
/*闭包可以在其被定义的上下文中捕获常量或变量,即使定义这些常量和变量的作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值
捕获值得闭包最简单形式是嵌套函数,嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
*/
func makeIncrementer(forIncrement amount: Int) -> () -> Int {//传入一个amount参数,返回值是一个无参Int型返回值的函数
var runningTotal = 0 //运行总数初始值为0
func incrementer() -> Int{//增量计算:该函数本身无参数传入,但从外围函数捕获了runningTotal、amount变量
runningTotal += amount
return runningTotal
}
return incrementer//返回incrementer函数
}
let incrementByTen = makeIncrementer(forIncrement: 10)//每次增加10
let incrementBySeven = makeIncrementer(forIncrement: 7)//每次增加7
incrementByTen()//10
incrementByTen()//20
incrementByTen()//30
incrementBySeven()//7
incrementByTen()//40
incrementBySeven()//14
//=======================闭包是引用类型=======================
//上面的incrementByTen 和 incrementBySeven 都是常量,但这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是引用类型。实际上可以理解为指向闭包的引用incrementBySeven 是一个常量,而并非闭包内容本身。
//这意味着,如果将闭包赋值给两个不同的常量或变量,两个值都会指向同一个闭包:
let alsoIncrementBySeven = incrementBySeven
alsoIncrementBySeven()//接着上面的处理,其值为21
//=======================逃逸闭包=======================
//逃逸闭包:闭包作为参数传递到函数中,在函数返回之后该闭包才被执行,这个闭包从函数中逃逸,被称为逃逸闭包。
//定义逃逸闭包:在函数的闭包参数名之前标注 @escaping 来表明这个闭包允许逃逸出这个函数
var completionArr:[() -> Void] = [] //定义了一个数组,该数组中存储的元素是闭包
func someFuncWithEscaping(completionHandler: @escaping () -> Void){//函数接受一个逃逸闭包作为参数,该闭包被添加到一个函数外定义的数组中
completionArr.append(completionHandler)
}
func someFuncNoneEscaping(closure:() -> Void){//函数接受一个非逃逸闭包
closure()
}
class SomeClass {//声明一个类
var x = 10
func doSometing(){//类中有一个方法,方法中调用了两个函数,第一个函数需要传递一个逃逸闭包的参数
someFuncNoneEscaping {//非逃逸闭包可以隐式引用self
x = 200
}
someFuncWithEscaping {//逃逸闭包需要显式引用self
self.x = 100
}
}
}
let instance = SomeClass()
instance.doSometing()
print(instance.x)//200
completionArr.first?()
print(instance.x)//100
//=======================自动闭包=======================
//自动闭包是一种自动创建的闭包,用于包装 传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。
//自动闭包能够延迟求值,直到调用这个闭包,闭包中的代码段才会被执行。
var nameArr = ["张三","李四","王二","麻子","大象"]
print(nameArr.count)//5
let removeValue = {//removeValue类型不是String,而是 () -> String 一个无参,返回值为String的函数
nameArr.remove(at: 0)//移除第0个元素,并将第0个元素赋值给removeValue
}
print(nameArr.count)//5,上面的函数没有被调用,所以数组没有发生移除事件,元素个数还是5
print("移除的人叫:\(removeValue())")//执行闭包
print(nameArr.count)//4,上一句调用了移除函数,数组元素变成了4
//闭包作为参数传递给函数
func serve(customer customerProvider: () -> String){//一个函数接收一个闭包作为参数
print("现在的值:\(customerProvider())")//打印其值
}
serve(customer: {nameArr.remove(at: 0)})//调用函数,并将闭包表达式作为参数传递到函数中,打印结果:现在的值:李四
//逃逸的自动闭包: 同时使用@autoclosure 和 @escaping 属性
var nameValueArr = ["轰天","战绩","潜艇","航母","飞机"]
var taoYiArr: [() -> String] = [] //元素为闭包的数组
func chuli(_ prams: @autoclosure @escaping () -> String){//逃逸的自动闭包,没有调用传入的prams闭包
taoYiArr.append(prams)//给数组添加元素,而是将闭包追加到taoYiArr中,在函数返回后才被调用
}
chuli(nameValueArr.remove(at: 0))//移除:轰天,调用函数,将表达式的值作为闭包传入到函数中
chuli(nameValueArr.remove(at: 0))//移除:战绩,调用函数,将表达式的值作为闭包传入到函数中
print(taoYiArr.count)//2,轰天、战绩
for taoYi in taoYiArr {
print(taoYi())//轰天、战绩
}
网友评论