Swift入门基础:闭包

发布于:2022-12-21 ⋅ 阅读:(202) ⋅ 点赞:(0)

之前一篇文章讲到了可选类型,它是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

以上就是本次闭包的讲解.

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到