swift中的闭包总结
闭包是功能性自包含模块,可以在代码中被传递和使用。 Swift 中的闭包与 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似。
闭包的基本语法
闭包表达式语法
1 |
{ (paramenters) -> returnType in
|
例如 : 我们将一堆字符串进行排序,闭包表达式版本的代码为:
1 |
let strs = ["wangju","libai","dumu","liuyong"]
var sortStrs = strs.sort ({ (s1 : String, s2 :String) -> Bool in
|
闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
闭包以参数的形式传递给了sort,从而确定排序的方式 ,形如: sort( {闭包} )
由于这个闭包比较短,我们可以将它改写成下面的方式 :
1 |
strs.sort ({ (s1 : String, s2 :String) -> Bool in return s1 < s2 })
|
闭包根据上下文推断类型
闭包函数是作为sort(_:)方法的参数传入的,swift可以根据传入的参数自动推断返回值类型
- s1 ,s2 可以根据字符串数组的调用推断为String类型
- 由此也可以推断返回值必须为Bool类型
这意味着(String, String)和Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略
1 |
strs.sort( { s1,s2 in return s1 < s2} )
|
单表达式闭包隐式返回
单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
1 |
strs.sort( { s1,s2 in s1 < s2} )
|
参数名缩写
Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。(这个和shell接收的参数类似)
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
1 |
strs.sort( { $0 < $1 } )
|
其中 $0 表示第一个参数 , $1 表示第二个参数,如果后面还有其他的参数,依次类推
运算符函数
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现:
1 |
sortStrs = strs.sort(<) |
尾随闭包
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:
例如定义一个接收一个闭包作为参数的函数 :
1 |
func (closure: () -> Void) {
}
|
- 不用尾随闭包的调用方式
1 |
// 以下是不使用尾随闭包进行函数调用 |
- 使用尾随闭包的调用方式
1 |
// 以下是使用尾随闭包进行函数调用 |
所以上面的sort函数可以根据下面的方法调用
1 |
strs.sort(){ s1,s2 in return s1 < s2}
|
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉:
1 |
strs.sort{ s1,s2 in return s1 < s2}
|
捕获值
闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
1 |
// 该函数接收一个Int类型的参数,并返回一个参数为空,返回值为Int类型的闭包 |
使用makeIncrementor
1 |
let incrementByTen = makeIncrementor(forIncrement: 10) incrementByTen() |
在返回的闭包还没有释放之前,swift会将捕获的值保存一份对值得拷贝,并自动管理捕获变量的生命周期
闭包是引用类型
这也意味着如果您将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:
非逃逸闭包
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注@noescape,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注@noescape能使编译器知道这个闭包的生命周期,使被传入的闭包不能被外界引用或者返回

将闭包标注为@noescape使你能在闭包中隐式地引用self。
1 |
// 闭包数组,用来保存someFunctionWithEscapingClosure传入的闭包用来以后用到的时候调用 |
测试结果
1 |
let instance = SomeClass() // doSomething 分别调用函数传入了两个闭包 |
自动闭包
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号。
1 |
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] |
尽管在闭包的代码中,customersInLine的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除
@autoclosure 可以将接收的表达式自动的包装为一个闭包
1 |
// serveCustomer 中用 @autoclosure 将接收的参数包装为一个() -> String类型的闭包 |
@autoclosure特性暗含了@noescape特性,默认创建的闭包是不可逃逸的,如果想要使闭包逃逸,可以使用@autoclosure(escaping)特性
1 |
customersInLine = ["Barry", "Daniella"] |
闭包的额外用法
函数的柯里化
Swift 里可以将方法进行柯里化 (Currying),也就是把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数并且返回结果的新方法
如两个数字求和
1 |
func addTwoNumbers(a: Int, num: Int) -> Int {
|
- 可以将该方法变为接收两个参数的方式(这种方式编译器实现了自动闭包)
1 |
func addTwoNumbers(a: Int) (_ num: Int) -> Int {
|
这种方法由于不好理解,会发出警告,估计马上会被官方弃用
- 第二种方法
1 |
// 接收一个判断的数值,并返回一个闭包 |
返回闭包类型的表示方式
同样是上面的代码
1 |
func greaterThan(comparor: Int) -> (input : Int) -> Bool{
|
上面的闭包可以隐藏形参名,变成下面这种方式
1 |
// 接收一个判断的数值,并返回一个闭包 |
我们也可以将传参的()一起忽略,变成最终这样,所以下面的返回值表示接收了一个Int的参数,并返回了一个Bool的闭包
1 |
// 接收一个判断的数值,并返回一个闭包 |
函数返回的箭头问题
函数中的箭头 -> 向右结合。这也就是说,你可以将 A -> B -> C 理解为 A -> (B -> C)
1 |
func add(x : Int) -> Int -> Int |
用自动闭包实现可选值??
?? 运算符。使用这个运算符时,需要额外提供一个默认值,当运算符被运用于 nil 时,这个默认值将被作为返回值。简单来说,它可以定义为下面这样
(该运算符官方默认已经实现)
1 |
func ??<T>(optional: T?, defaultValue: T) -> T {
|
?? 运算符会检验它的可选参数是否为 nil。如果是,返回 defaultValue 参数;否则,返回可选值中实际的值。
optional ?? defaultValue
例如
1 |
let dict = ["name":"wagju","age":"20"]
var optionalHeight: Double? {
|
在这个例子中,default的运算在传参的时候已经被执行,如果 optional 变量是非 nil 的话,我们真的不愿意对 defaultValue 进行求值 —— 因为这可能是一个开销非常大的计算,只有绝对必要时我们才会想运行这段代码。可以按如下方式解决这个问题:
1 |
func ??<T>(optional: T?, defaultValue: () -> T) -> T {
|
以上代码把defaultValue的运算延迟到用到defaultValue并且需要返回的时候
但是这样写的话我们必须要传递一个闭包
1 |
myOptional ?? { myDefaultValue }
|
1 |
class ACell |
所以,我们可以使用隐式闭包来定义??,从而实现有选择的执行传值问题
1 |
infix operator ?? { associativity right precedence 110 }
|
swift中的闭包总结的更多相关文章
- Swift中的闭包(Closure) 浅析
转载自:http://www.devtalking.com/articles/closure-expressions-in-swift/ 闭包在Swift中非常有用.通俗的解释就是一个Int类型里存储 ...
- Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其它一些编程语言中的 lambdas 比較类似。
闭包是功能性自包括模块,能够在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其它一些编程语言中的 lambdas 比較相似. 闭包能够 捕获 和 ...
- Swift中的闭包(Closure)[转]
闭包在Swift中非常有用.通俗的解释就是一个Int类型里存储着一个整数,一个String类型包含着一串字符,同样,闭包是一个包含着函数的类型.有了闭包,你就可以处理很多在一些古老的语言中不能处理的事 ...
- Swift中方法闭包参数不能省略括号的一种情况
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们知道在swift中,如果方法的最后一个参数是一个闭包类型, ...
- [Swift]UIAlertController 以及 Swift 中的闭包和枚举
原文地址:http://blog.callmewhy.com/2014/10/08/uialertcontroller-swift-closures-enum/ 在 iOS8 的 SDK 中, UIK ...
- SwiftCafe 咖啡时光 - 了解 Swift 中的闭包
闭包(Closure) 是现代开发语言的必备特性,极大的提高了我们的开发效率. 关于闭包,你可以把它理解为一种特殊的变量或对象.简而言之,我们通常的对象,里面存储的是变量或对象的值,而闭包里面存储的是 ...
- Swift: 比较Swift中闭包传值、OC中的Block传值
一.介绍 开发者对匿名函数应该很清楚,其实它就是一个没有名字的函数或者方法,给人直观的感觉就是只能看到参数和返回值.在iOS开发中中,它又有自己的称呼,在OC中叫Block代码块,在Swift中叫闭包 ...
- iOS开发之OC与swift开发混编教程,代理的相互调用,block的实现。OC调用Swift中的代理, OC调用Swift中的Block 闭包
本文章将从两个方向分别介绍 OC 与 swift 混编 1. 第一个方向从 swift工程 中引入 oc类 1. 1 如何在swift的类中使用oc类 1.2 如何在swift中实现oc的代理 ...
- swift中闭包的学习。
在swift中的闭包等同于OC中的block,它的用途就是在于可以包装一段代码在必要的时候进行调用. 闭包定义: {(类型列表) -> 返回值 in // 多条swift语句 // 执行代码 ...
随机推荐
- Python这五个坑,80%你不知道(对的,五个你知道1个就达到一般水平了)
1 含单个元素的元组 Python中有些函数的参数类型为元组,其内有1个元素,这样创建是错误的: c = (5) # NO! 它实际创建一个整型元素5,必须要在元素后加一个逗号: ! c = (5,) ...
- 对Spring aware理解
aware翻译过来时就是意识到,我对他的理解就是spring的感知器.是不是很诡异这个名字起得^_^ 先来看看aware接口的结构 spring提供了许多的aware,Aware.java也只是做一个 ...
- ERP上线通用模板
一.引言 随着现代信息技术的发展与广泛应用,现代社会的快速发展和越来越追求效率的现状,对各行各业的管理的水平有了进一步的提高.XX企业作为我国的国民经济发展的基础产业,其信息化建设的水平直接关系 ...
- springboot的linux-docker部署
将springboot jar应用打包成镜像并在docker运行成容器 https://blog.csdn.net/keepd/article/details/80569797 Docker安装(De ...
- Java统计内存
在目标代码前放置 Runtime r = Runtime.getRuntime(); r.gc(); long startMem = r.freeMemory(); // 开始时的剩余内存 目标代码执 ...
- Opencv笔记(十三)——图像的梯度
目标 认识图像梯度.边界 学习函数cv2.Sobel(),cv2.Schar(),cv2.Laplacian() 原理 图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导.Op ...
- HEX 文件格式
例 FDFFF885C3 :每行开头 第一个字节:表示本行的数据长度, 第二个,第三个字节表示本行数据的起始地址. 第四字节表示数据类型,数据类型有:0x00.0x01.0x02.0x03.0x04. ...
- 引入插件的时候 提示particlesJS is not defined
particlesJS is not defined 插件或者js文件在引入时需要注意引入顺序,每次都找很久的错误 一般引入min.js就可以,min.js意思就是压缩的js文件 引入时应该先加入 ...
- 使用lambda表达式优雅你的事务代码
我们在实现业务逻辑时,经常会有这种需求: 1.在当前事务A中开启新事务B,事务B中发生异常不可以回滚A,事务B成功执行后返回事务B的返回值: 2.在当前事务A中开启新事务B,事务B中发生异常要求回滚A ...
- vue2.XX 提示[Vue warn]: Error in render: "TypeError: Cannot read property 'img' of undefined"
item 是向后台请求的一条数据,里面包含img,但是却提示img未定义 父组件向子组件传递数据时, 子组件 具体代码: <img :src="item.img" /> ...