文档章节

Swift讲解专题八——闭包

珲少
 珲少
发布于 2016/05/14 22:09
字数 1747
阅读 841
收藏 17

Swift讲解专题八——闭包

一、引言

        Swift中的闭包是有一定功能的代码块,这十分类似于Objective-C中的block语法。Swift中的闭包语法风格十分简洁,其作用和函数的作用相似。

二、从一个系统函数看闭包

        Swift标准函数库中提供了一个sort排序函数,对于已经元素类型的数组,调用sort函数会进行重新排序并返回新的排序后的数组。这个sort函数可以接收一个返回值为Bool类型的闭包,来确定第一个元素是否排在第二个元素前面。代码示例如下:

var array = [3,21,5,2,64]
func func1(param1:Int,param2:Int) -> Bool {
    return param1>param2
}
//通过传入函数的方式
//array = [64,21,5,3,2]
array = array.sort(func1)
//通过闭包的方式
//array = [2,3,5,21,64]
array = array.sort({(param:Int,param2:Int)->Bool in
                        return param<param2
                    })

Swift语言有一个很显著的特点就是简洁,可以通过上下文推断出类型的情况一般开发都可以将类型的书写省略,这也是Swift语言设计的一个思路,由于闭包是作为函数的参数传入函数中的,因为函数参数的类型是确定,因此闭包的类型是可以被编译器推断出来的,开发者也可以将闭包的参数类型和返回值省略,上面的代码可以简写如下:

//将闭包的参数类型和返回值都省略
array = array.sort({(p1,p2) in return p1>p2})

实际上,如果闭包中的函数体只有一行代码,可以将return关键字也省略,这时会隐式的返回此行代码的值,如下:

array = array.sort({(p1,p2) in   p1>p2})

看到上面的表达式,是不是有点小震惊,闭包表达式竟然可以简写成这样!然而,你还是小看的Swift开发团队,后面的语法规则会让你明白什么是简洁的极致。可以看到上面的代码实现还是有3部分:参数和返回值,闭包关键字,函数体。参数和返回值即是参数列表,p1,p2,虽然省略了参数类型和返回值类型,但这部分的模块还在,闭包关键字即是in,它用来表示下面将是闭包的函数体,p1>p2即是函数体,只是这里省略了return关键字。闭包中既然参数类型和返回值类型编译器都可以自己推断出来,那么参数的数量编辑器也是可以自行推断的,因此,参数列表实际上也是多余的,闭包中会自动生成一些参数名称,和实际的参数数量向对应,例如上面sort函数中的闭包有两个参数,系统会自动生成$0和$1这两个参数名,开发者可以直接使用,因为参数列表都会省略了,那么也不再需要闭包关键字in来分隔参数列表与函数体,这时,闭包的写法实际上变成了如下的模样:

array = array.sort({$0<$1})

你没有看错,加上左右的大括号,一共7个字符,完成了一个排序算法。除了Swift,我不知道是否还有第二种语言可以做到。抛开闭包不说,Swift中还有一种语法,其可以定义类型的运算符方法,例如String类型可以通过=,<,>来进行比较,实际上是String类中实现了这些运算符方法,在某种意义上说,一个运算符即类似与一个函数,那么好了,sort函数中需要传入的方法对于某些类型来说实际上只是需要一个运算符,示例如下:

array = array.sort(>)

这次你可以真的震惊了,完成排序新算法只需要一个字符,不折不扣的一个字符。

三、Swift中闭包的更多特点

        Swift中的闭包还有一个有趣的特点,首先闭包是作为参数传入另一个函数中的,因此常规的写法是将闭包的大括号写在函数的参数列表小括号中,如果闭包中的代码很多,这时在代码结构上来看会变得并不太清晰,为了解决这个问题,Swift中这样规定:如果这个闭包参数是函数的最后一个参数,开发者可以将其拉出小括号,在函数尾部实现闭包代码,示例如下:

//闭包结尾
func func2(param1:Int,param2:()->Void)->Void{
    param2()
    print("调用了func2函数")
}
func2(0){
        print("闭包中的内容")
}

如果一个函数中只有一个参数,且这个参数是一个闭包,那么开发者使用闭包结尾这种写法,完全可以将函数的参数列表小括号也省略掉,示例如下:

func func3(param:()->Void)->Void{
    param()
    print("调用了func3函数")
}
func3{
    print("闭包中的内容")
}

Swift中还有一个闭包逃逸的概念,这个很好理解,当闭包作为参数传递进函数时,如果这个闭包只在函数中被使用,则开发者可以将这个闭包声明成非逃逸的,即告诉系统当此函数结束后,这个闭包的声明周期也将结束,这样做的好处是可以提高代码性能,将闭包声明称非逃逸的类型使用@noescape关键字,示例如下:

func func3(@noescape param:()->Void)->Void{
    param()
    print("调用了func3函数")
}
func3{
    print("闭包中的内容")
}

逃逸的闭包常用于异步的操作,例如这个闭包是异步处理一个网络请求,只有当请求结束后,闭包的声明周期才结束。非逃逸的闭包还有一个有趣的特点,在其内部如果需要使用self这个关键字,self可以被省略。

        闭包也可以被自动的生成,这种闭包被称为自动闭包,自动闭包可以自动将表达式封装成闭包,开发者不需要再写闭包的大括号格式,自动闭包不接收参数,返回值为其中表达式的值。示例如下:

//自动闭包演示
var list = [1,2,3,4,5,6]
//创建一个显式闭包
let closures = {
    list.removeFirst()
    list.append(7)
}
//将打印[1,2,3,4,5,6]
print(list)
//执行闭包
closures()
//将打印[2,3,4,5,6,7]
print(list)
func func4(closure:()->Void) -> Void {
    //执行显式的闭包
    closures()
}
func func5(@autoclosure auto:()->Void) -> Void {
    //执行自动闭包
    auto()
}
//显式闭包 需要大括号
func4(closures)
//将打印[3,4,5,6,7,7]
print(list)
//将表达式自动生成闭包
func5(list.append(8))
//将打印[3,4,5,6,7,7,8]
print(list)

自动闭包默认是非逃逸的,如果要使用逃逸的闭包,需要手动声明,如下:

func func5(@autoclosure(escaping) auto:()->Void) -> Void {
    //执行自动闭包
    auto()
}

专注技术,热爱生活,交流技术,也做朋友。

——珲少 QQ群:203317592

© 著作权归作者所有

共有 人打赏支持
珲少

珲少

粉丝 857
博文 384
码字总数 452377
作品 0
上海
iOS工程师
私信 提问
加载中

评论(3)

珲少
珲少

引用来自“珲少”的评论

引用来自“ios122”的评论

前端童鞋笑了: 闭包是一种技术,你说的闭包,其实就是匿名函数;block,最多算是 匿名函数的一个别名

各种需要的技术术语不同吧,官方的说法叫Closures,我相信大多数开发者都会翻译成闭包,至于block,是C和OC的语法,不会有任何意义,像C++中的lambda,当然你可以理解他们都是匿名函数,但是术语如此,本该如此

语言的技术术语,不会有任何异意,不好意思,输入法太不给力
珲少
珲少

引用来自“ios122”的评论

前端童鞋笑了: 闭包是一种技术,你说的闭包,其实就是匿名函数;block,最多算是 匿名函数的一个别名

各种需要的技术术语不同吧,官方的说法叫Closures,我相信大多数开发者都会翻译成闭包,至于block,是C和OC的语法,不会有任何意义,像C++中的lambda,当然你可以理解他们都是匿名函数,但是术语如此,本该如此
ios122
ios122
前端童鞋笑了: 闭包是一种技术,你说的闭包,其实就是匿名函数;block,最多算是 匿名函数的一个别名
Swift专题讲解十九——类型转换

Swift专题讲解十九——类型转换 一、类型检查与转换 在Objective-C和Java中,任何类型实例都可以通过强转使编译器认为它是另一种类型的实例,这么做其实是将所有的安全检查工作都交给了开发者...

珲少
2016/05/27
58
0
Swift专题讲解十六——ARC在Swift中的应用

Swift专题讲解十六——ARC在Swift中的应用 一、引言 ARC(自动引用计数)是Objective-C和Swift中用于解决内存管理问题的方案。在学习Objective-C编程时经常会学习到一个关于ARC的例子:在一个...

珲少
2016/05/20
1K
1
Swift解读专题一——Swift2.2语言预览

专题一——Swift2.2语言预览 一、引言 本系列专题是我通过阅读Swift2.2语言开发文档,翻译总结加上自己的理解整理而成。其中大部分结构和内容都来自开发文档,有疏漏和错误之处,还望更多朋友...

珲少
2016/05/05
825
0
Swift专题讲解二十——扩展

Swift专题讲解二十——扩展 一、简介 Swift中的扩展与Objective-C中的类别功能相似,扩展可以为一个已有的类、结构体、枚举或者协议添加新的属性或方法,与Objective-C的类别不同的是,Swift...

珲少
2016/05/29
99
0
Swift专题讲解十五——类型构造

Swift专题讲解十五——类型构造 一、引言 构造是类、结构体、枚举在实例化中必须执行的过程,在构造过程中,类、结构体必须完成其中存储属性的构造。Swift中的构造通过构造方法来完成,和Obj...

珲少
2016/05/19
364
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 你一口我一口多咬一口是小狗

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @达尔文 :分享Roy Orbison的单曲《She's a Mystery to Me》 《She's a Mystery to Me》- Roy Orbison 手机党少年们想听歌,请使劲儿戳(这里...

小小编辑
29分钟前
10
2
Spring源码学习笔记-1-Resource

打算补下基础,学习下Spring源码,参考书籍是《Spring源码深度解析》,使用版本是Spring 3.2.x,本来想试图用脑图记录的,发现代码部分不好贴,还是作罢,这里只大略记录下想法,不写太细了 ...

zypy333
今天
12
0
RestClientUtil和ConfigRestClientUtil区别说明

RestClientUtil directly executes the DSL defined in the code. ConfigRestClientUtil gets the DSL defined in the configuration file by the DSL name and executes it. RestClientUtil......

bboss
今天
18
0

中国龙-扬科
昨天
2
0
Linux系统设置全局的默认网络代理

更改全局配置文件/etc/profile all_proxy="all_proxy=socks://rahowviahva.ml:80/"ftp_proxy="ftp_proxy=http://rahowviahva.ml:80/"http_proxy="http_proxy=http://rahowviahva.ml:80/"......

临江仙卜算子
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部