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

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

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: