博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift 闭包整理
阅读量:5955 次
发布时间:2019-06-19

本文共 5168 字,大约阅读时间需要 17 分钟。

hot3.png

语法表达式

1

2

3

4

一般形式:{

            (parameters) -> returnType in

             statements

           }

  • 这里的参数(parameters),可以是in-out(输入输出参数),但不能设定默认值。如果是可变参数,必须放在最后一位,不然编译器报错。元组也可以作为参数或者返回值。

  • "in"关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。即由in引入函数

  • 例子

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//一般形式

let calAdd:(Int,Int)->(Int) = {

    (a:Int,b:Int) -> Int in

    return a + b

}

print(calAdd(100,150))

 

//Swift可以根据闭包上下文推断参数和返回值的类型,所以上面的例子可以简化如下

let calAdd2:(Int,Int)->(Int) = {

    a,b in  //也可以写成(a,b) in

    return a + b

}

print(calAdd2(150,100))

//上面省略了返回箭头和参数及返回值类型,以及参数周围的括号。当然你也可以加括号,为了好看点,看的清楚点。(a,b)

 

//单行表达式闭包可以隐式返回,如下,省略return

let calAdd3:(Int,Int)->(Int) = {(a,b) in a + b}

print(calAdd3(50,200))

 

//如果闭包没有参数,可以直接省略“in”

let calAdd4:()->Int = {

return 100 + 150}

print("....\(calAdd4())")

 

//这个写法,我随便写的。打印出“我是250”

//这个是既没有参数也没返回值,所以把return和in都省略了

let calAdd5:()->Void = {print("我是250")}

calAdd5()

  • 归纳

    闭包类型是由参数类型和返回值类型决定,和函数是一样的。比如上面前三种写法的闭包的闭包类型就是(Int,Int)->(Int),后面的类型分别是()->Int()->Void。分析下上面的代码:let calAdd:(add类型)。这里的add类型就是闭包类型 (Int,Int)->(Int)。意思就是声明一个calAdd常量,其类型是个闭包类型。

    "="右边是一个代码块,即闭包的具体实现,相当于给左边的add常量赋值。兄弟们,是不是感觉很熟悉了,有点像OC中的block代码块。

起别名

  • 也可以关键字“typealias”先声明一个闭包数据类型。类似于OC中的typedef起别名

1

2

3

4

5

6

7

8

9

typealias AddBlock = (Int, Int) -> (Int)

 

let Add:AddBlock = {

    (c,d) in

    return c + d

}

 

let Result = Add(100,150)

print("Result = \(Result)")

尾随闭包

  • 若将闭包作为函数最后一个参数,可以省略参数标签,然后将闭包表达式写在函数调用括号后面

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

func testFunction(testBlock: ()->Void){

    //这里需要传进来的闭包类型是无参数和无返回值的

    testBlock()

}

//正常写法

testFunction(testBlock: {

    print("正常写法")

})

//尾随闭包写法

testFunction(){

    print("尾随闭包写法")

}

//也可以把括号去掉,也是尾随闭包写法。推荐写法

testFunction { 

    print("去掉括号的尾随闭包写法")

}

值捕获

  • 闭包可以在其被定义的上下文中捕获常量或变量。Swift中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

func captureValue(sums amount:Int) -> ()->Int{

    var total = 0

    func incrementer()->Int{

        total += amount

        return total

    }

    return incrementer

}

 

print(captureValue(sums: 10)())

print(captureValue(sums: 10)())

print(captureValue(sums: 10)())

//打印"10 10 10"

这里没有值捕获的原因是,没有去用一个常量或变量去引用函数,所以每次使用的函数都是新的。有点类似于OC中的匿名对象。

1

2

3

4

5

let referenceFunc = captureValue(sums: 10)

print(referenceFunc())

print(referenceFunc())

print(referenceFunc())

//打印"10 20 30"

这里值捕获了,是因为函数被引用了,所以没有立即释放掉。所以函数体内的值可以被捕获

  • 闭包形式

1

2

3

4

5

6

7

8

9

10

11

12

13

func captureValue2(sums amount:Int) -> ()->Int{

    var total = 0

    let AddBlock:()->Int = {

        total += amount

        return total

    }

    return AddBlock

}

 

let testBlock = captureValue2(sums: 100)

print(testBlock())

print(testBlock())

print(testBlock())

由上面的例子都可以证得,函数和闭包都是引用类型。

逃逸闭包

  • 当一个闭包作为参数传到一个函数中,需要这个闭包在函数返回之后才被执行,我们就称该闭包从函数种逃逸。一般如果闭包在函数体内涉及到异步操作,但函数却是很快就会执行完毕并返回的,闭包必须要逃逸掉,以便异步操作的回调。

  • 逃逸闭包一般用于异步函数的回调,比如网络请求成功的回调和失败的回调。语法:在函数的闭包行参前加关键字“@escaping”。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

//例1

func doSomething(some: @escaping () -> Void){

    //延时操作,注意这里的单位是秒

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {

        //1秒后操作

        some()

    }

    print("函数体")

}

doSomething {

    print("逃逸闭包")

}

 

//例2

var comletionHandle: ()->String = {

"约吗?"}

 

func doSomething2(some: @escaping ()->String){

    comletionHandle = some

}

doSomething2 {

    return "叔叔,我们不约"

}

print(comletionHandle())

 

//将一个闭包标记为@escaping意味着你必须在闭包中显式的引用self。

//其实@escaping和self都是在提醒你,这是一个逃逸闭包,

//别误操作导致了循环引用!而非逃逸包可以隐式引用self。

 

//例子如下

var completionHandlers: [() -> Void] = []

//逃逸

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {

    completionHandlers.append(completionHandler)

}

//非逃逸

func someFunctionWithNonescapingClosure(closure: () -> Void) {

    closure()

}

 

class SomeClass {

    var x = 10

    func doSomething() {

        someFunctionWithEscapingClosure { self.x = 100 }

        someFunctionWithNonescapingClosure { x = 200 }

    }

}

自动闭包

  • 顾名思义,自动闭包是一种自动创建的闭包,封装一堆表达式在自动闭包中,然后将自动闭包作为参数传给函数。而自动闭包是不接受任何参数的,但可以返回自动闭包中表达式产生的值。

  • 自动闭包让你能够延迟求值,直到调用这个闭包,闭包代码块才会被执行。说白了,就是语法简洁了,有点懒加载的意思。

1

2

3

4

5

6

7

8

9

10

11

12

13

var array = ["I","have","a","apple"]

print(array.count)

//打印出"4"

 

let removeBlock = {array.remove(at: 3)}//测试了下,这里代码超过一行,返回值失效。

print(array.count)

//打印出"4"

 

print("执行代码块移除\(removeBlock())")

//打印出"执行代码块移除apple" 这里自动闭包返回了apple值

 

print(array.count)

//打印出"3"

 

 

5. 非逃逸闭包(Nonescaping Closures)

一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。可以在参数名之前标注@noescape,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注@noescape能使编译器知道这个闭包的生命周期.

像刚才的数组的sort(_:)函数中的参数就定义成了非逃逸闭包,

public func sort(@noescape isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element]

你可能会问什么时候会出现逃逸闭包呢?举个例子:很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。

非逃逸闭包和逃逸闭包讲的不是执行先后顺序吧,非逃逸是指你的闭包不能在函数外单独调用,只能在函数内部调用,函数调用完成后,那个闭包也就结束了。

下面举个逃逸闭包的例子:

//声明一个存放函数的数组var functionArray: [() -> Void] = []//定义一个接收闭包参数的函数,如果定义非逃逸函数 func doSomething(@noescape paramClosure:() -> Void) 就会编译错误func doSomething(paramClosure:@escaping () -> Void){    //把参数放入数组中,用于逃逸调用    functionArray.append(paramClosure)    }//调用函数doSomething(paramClosure: {print("Hello world")})doSomething(paramClosure: {print("Hello LvesLi")})//逃逸调用闭包for closurePrama in functionArray {        print("\(closurePrama)")    }

 

 

 

转载于:https://my.oschina.net/iceTear/blog/1586142

你可能感兴趣的文章
MathType输入框怎么调整
查看>>
几何画板要怎样度量直线方程
查看>>
MathType中如何快速输入空心字母
查看>>
hashMap 和linkedHashMap
查看>>
DFA和NFA的区别
查看>>
数据转换成json传递
查看>>
Docker(六)安装Red5进行rtmp推流
查看>>
[hadoop新实战4]hadoop完全分布式安装序列(支持ubuntu和redhat)
查看>>
spring框架总结
查看>>
我的友情链接
查看>>
Centos之LAMP环境搭建
查看>>
Aptana 添加jQuery提示
查看>>
lr 远程压力机部署安装
查看>>
mui调本地相册上传图片
查看>>
后缀表达式求值
查看>>
.NET Framework也可以开发托管了
查看>>
noSql-redis
查看>>
centos crontab环境变量问题
查看>>
Android视频播放
查看>>
Linux curl命令简介
查看>>