之前一篇文章讲到了可选类型,它是Swift中新增加的一个概念。今天讲的闭包也是其中一个,那什么是闭包呢?今天我们讲一下。
概念
闭包,其概念是:“自包含的功能块,可以在代码中传递和使用”。这个不好理解。转换下,其实,闭包类似于Objective-C中的Block,它就是一个具有一定功能的代码块;或者按Java语言来说,是一个表达式(lambdas表达式)。
闭包表达式语法
标准形式为:
{
(参数列表)->返回值类型 in 函数体代码
}
标准形式语法的注意点:
1.一般以左大括号开头,右大括号结束,中间为闭包内容。
2.参数是列表形式,可以有多个参数,若多个参数用逗号隔开
3.in是一个分隔符,将函数体和参数、返回值分隔开来。
4.返回值类型,关键字in是可以省略的。
各种表达式实例举例:
//1.闭包是一个标准的书写格式,有参数,有返回值
let sum:(Int,Int) = {(num1:Int,num2:Int) -> Int in
return num1 + num2
}
printf(sum(10,20))
//2.闭包省略了返回值
let sum:(Int,Int) = {(num1:Int,num2:Int) in
return num1 + num2
}
printf(sum(10,20))
//3.闭包省略了返回值,括号
let sum:(Int,Int) = {num1,num2 in
return num1 + num2
}
printf(sum(10,20))
//4.闭包省略了返回值,括号,return关键字
let sum:(Int,Int) = {num1,num2 in
num1 + num2
}
printf(sum(10,20))
//5.闭包省略了返回值,括号,return关键字,in关键字
let sum:(Int,Int) = {
$0 + $1
}
printf(sum(10,20))
//6.甚至,在sort表达式中只写一个操作符。
let oldArr = [1,5,3,2,7,4,6,8]
var newArr = oldArr.sorted(by: >)
print(newArr)
小结:
闭包的缩写形式很多,不必每个形式都要记住。只要按标准格式编写闭包代码就好,其他格式不建议书写;另外阅读别人的代码,知道这种形式是闭包就好。
尾随闭包
尾随闭包是一个书写在函数括号之后的闭包表达式,一般用在函数的最后一个参数上。
使用尾随闭包作为函数的最后一个实参,可以增强代码的可读性。
//没有使用尾随闭包的形式,就相当于函数的参数内嵌套一个函数。
func sum(a:Int,b:Int) -> Int {
return a + b
}
func getAllValue(a:Int,b:Int,sum:(Int,Int) -> Int {
sum(a,b)
}
//使用尾随闭包形式
func getAllValue(a:Int,b:Int,{(Int,Int) -> Int in
return a + b
})
//好处:缩减成一个函数,简化了代码。
逃逸闭包
若一个闭包是函数的参数,并且在函数执行完之后才执行,那么这个闭包就被称为逃逸闭包。
一般在参数名的后面用@escaping关键字来修饰逃逸闭包。
逃逸闭包一般用与异步操作,特别是在网络请求。例如异步下载网络图片,下载成功后通知界面刷新的场景。
class ImageLoader {
var completion:(UIImage) -> Void
var failure:() -> Void
//初始化成功和失败的逃逸闭包,并不是想在初始化的时候执行这两个闭包,而是等下载成功后,在异步执行
init(completion @escaping (UIImage) -> Void,failure @escaping (String) ->Void) {
self.completion = completion
self.failure = failure
}
//从网络上下载图片并转化成UIImage格式
func getImageDataFormNetwork(url:String) -> UIImage? {
//使用Alarmofire,从网络上获取图片数据
//.....
return UIImage()
}
//发起url请求,并处理返回图片数据
func loadImage(url:String) {
if let image = getImageDataFormNetwork(url) {
self.completion(image)
} else {
self.failure()
}
}
}
//实例化对象
var imageLoader = ImageLoader(completion: { image in
print(image)
},failure: {
print("load failed")
})
//调用接口请求数据
imageLoader.loadImage("http://xxxx.jpg")
闭包捕获值
闭包能够从上下文中捕获已被定义的常量或变量,即使这些常量或变量的作用域不存在了,闭包仍能够在其函数体内引用或修改这些值。如下实例:
func makeIncrementer(count:Int) ->() ->Int {
var total = 0
func incrementer() -> Int {
total += count
return total
}
return incrementer
}
let increment10 = makeIncrementer(10)
increment10() //10
increment10() //20
increment10() //30
//从上面结果看出,total是一直在累加的。
//重新生成新变量,那么又从0开始
let increment20 = makeIncrementer(10)
increment20() //10
increment20() //20
//若在调用原来的increment10,那继续从原来的30累加
increment10() //40
这就说明其捕获值与变量的生命周期不关。
另外注意:
1.闭包捕获时机是在函数体执行完成,return之后再捕获。
2.当函数里有多个闭包时,只会对变量、常量捕获一次;多个闭包对捕获的变量、常量是共享的。
3.闭包不会对全局变量进行捕获。
闭包是引用类型
闭包是引用类型。即将闭包赋值给一个变量,实际上都是将变量的值设置为对应闭包的引用。也就是说,变量是指向闭包的引用地址。
上面的例子中,increment10指向闭包的引用是一个常量,而并非闭包内容本身。这也意味着如果您将闭包赋值给了两个不同的常量,两个值会指向同一个闭包。若把上述的increment10赋值给新的常量并对新常量调用,那么也会改变旧的数据。如下所示。
let otherIncrement10 = increment10()
otherIncrement10() // 50
otherIncrement10() // 60
increment10() // 70
以上就是本次闭包的讲解.
推荐链接
发表评论