Swift 中的Closures(闭包)详解

在Swift没有发布之前,所有人使用OC语言编写Cocoa上的程序,而其中经常被人们讨论的其中之一 -- Block 一直备受大家的喜爱。在Swift中,同样有这样的一个角色,用于当开发者需要异步执行的之后使用的一种语法 - Closure。中文翻译为闭包。

闭包出了可以进行异步执行之外,它的完整使用还依赖闭包本身的变量、常量的捕获。闭包捕获并存储对它们定义的上下文中的任何常量和变量的引用,这也就意味着,你可以在任何时候异步执行闭包的时候获取之前的所有的环境变量。而实际上,闭包类似于Swift 中的匿名函数,在上一篇文章中,介绍了高阶函数和嵌套函数,它们和闭包有者不可分割的一些联系。比如,最简单的闭包起始就是一个高阶函数,只是在闭包做为参数变量的时候,闭包是匿名、书写时实现。当对于一般的高阶函数,闭包更轻量级。

本文介绍几种闭包的形式,以及一些闭包的特性。

一、闭包的基本形式

这是一个最基本的闭包的形式:

{ (parameters) -> return type instatements
}

闭包中,包含三要素: 参数,返回类型,闭包体。  其中参数和返回类型可以忽略, 但是一个闭包体必需存在,实际上就算在闭包体里面,什么都不写,闭包体本身还是以不执行任何代码的形式存在。

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool inreturn s1 > s2
})

这是一个高阶函数,同时,也是一个闭包的基本使用。包含了 参数及类型,返回值类型,闭包体。

我们可以简写闭包的形式,采用内联的方式书写:

 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

在闭包中,因为包含了上下文的变量和常量的引用,并做了类型推断,所以,实际上,对于闭包的参数来说,类型是固定的,当然返回的类型也是固定的,swift允许开发者书写时省略。就像下面这样:

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

如果闭包是的闭包体是单个表达式(只有一条执行语句)的时候,甚至可以将return都省略

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

另外swift的闭包又一个特性:Swift自动为内联闭包提供简写参数名称,可用于通过名称$ 0,$ 1,$ 2等引用闭包参数的值。

如果在闭包表达式中使用这些简写参数名称,则可以从其定义中省略闭包的参数列表,并根据预期的函数类型推断速记参数名称的数量和类型。 in关键字也可以省略,因为闭包表达式完全由其主体组成:

reversedNames = names.sorted(by: { $0 > $1 } )

这样还不够完美,如果将” > “符号重载,使  $0 > $1 直接用 > 代替是不是更加简洁呢? 当然可以,事实上,swift中的 > 已经被定义了 > 指代两个Swift string之间的比较,并返回一个 Bool值。那么我们的最终版本应该是这样的:

reversedNames = names.sorted(by: >)

二、尾随闭包

如果你定义了一个闭包,将之作为一个函数的最后的一个参数,并且,由于之前还包含了其他的参数,导致这个函数变得很长,你可以使用一种简短的方式书写闭包。像下面这样的函数:

func someFunctionThatTakesAClosure(closure: () -> Void) {// function body goes here
}

如果我们调用的使用,应该是这样的:

someFunctionThatTakesAClosure(closure: {// closure's body goes here
})

像这样的情况,我们考虑它构成了尾随闭包,对于尾随闭包,有另一中更加简便的方式书写:

someFunctionThatTakesAClosure() {// trailing closure's body goes here
}

注意,这时候,闭包的标签   closure  应该被省略。

这样的情况,我们可以写出闭包的最终版本的特别版本:

reversedNames = names.sorted(>)

尾随闭包使用在一个长度很长的闭包下,效果更好。你可以使用这个技巧写出很简短优雅的代码。

三、闭包捕获值

之前其实已经介绍了,闭包的本质类似于一个嵌套函数,它具有和嵌套函数一样能力:获取局部的变量和常量,在一个闭包中:它可以获取到外部函数所有参数,和外部函数内定义的任何的常量和变量。

这里有个嵌套的函数,他和闭包的作用一样:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {var runningTotal = 0func incrementer() -> Int {runningTotal += amountreturn runningTotal}return incrementer
}

incrementer 可以获取到amount和runingTotal的值来使用。

调用:

let incrementByTen = makeIncrementer(forIncrement: 10)    //绑定引用, 保证用完不会消失。
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30

incrementer 这个嵌套的函数一值在改变runningTotal的值,同时接收不断变化的amount。

如果换一个函数变量引用,那么值则会被刷新:

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7

闭包的引用具有关联性。和一般的变量一样:

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50

四、逃逸闭包

在网络请求的过程中,大部分时候都是异步操作的,一般的闭包,虽然已经定义了闭包的闭包体(闭包实现),但是在请求回调的时候,闭包可能不会被调用。原因是可能被释放, 或者可能被修改。

如果需要保证做到异步的调用,那么需要在闭包的参数前加 @escaping标记。

添加@escaping标记的闭包,意味着,如果需要访问自身的自身的变量的时候,必须在闭包内包含自身的引用(或者自身的属性、变量等)。

下面是两种是否带标签的闭包的对比:

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {completionHandlers.append(completionHandler)
}

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

两者调用:

class SomeClass {var x = 10func doSomething() {someFunctionWithEscapingClosure { self.x = 100 }someFunctionWithNonescapingClosure { x = 200 }}
}

默认的闭包都是 非逃逸闭包。

五、Autoclosures 自动闭包

自动闭包的意思是当闭包做为函数的参数的时候,对于闭包的内容执行的时机取决于谁。  普通的闭包,将会在读取闭包的时候执行,而自动闭包,在读取的时候,将闭包做为其参数类型处理,只有等到执行闭包的时候才会处理闭包内的内容。

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"

在执行之前,customerProvider被当作一个 () -> String 处理,而不会涉及到闭包体内部的处理。 如果不使用@autoclosure,那么函数处理闭包体内的内容。

日常的开发可以使用:

public func Dlog( item:@autoclosure ()->Any){#if DEBUGprint(item())#else#endif
}

默认的自动闭包是 非逃逸闭包,如果想变成逃逸闭包,可以如下:

func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {customerProviders.append(customerProvider)
}

转载于:https://www.cnblogs.com/FBiOSBlog/p/7593921.html

Swift 中的Closures(闭包)详解相关推荐

  1. Swift 中的内存管理详解

    这篇文章是在阅读<The Swift Programming Language>Automatic Reference Counting(ARC,自动引用计数)一章时做的一些笔记,同时参考 ...

  2. JavaScript重难点解析4(作用域与作用域链、闭包详解)

    JavaScript重难点解析4(作用域与作用域链.闭包详解) 作用域与作用域链 作用域 作用域与执行上下文 作用域链 闭包 闭包理解 将函数作为另一个函数的返回值 将函数作为实参传递给另一个函数调用 ...

  3. 【JavaScript 教程】ES6 中的 Promise对象 详解

    [JavaScript 教程]ES6 中的 Promise对象 详解 1.Promise对象含义 promise是异步编程的一种解决方法. 所谓promise,简单说是一个容器,里面保存着某个未来才会 ...

  4. JavaScript闭包详解及案例

    JavaScript闭包详解及案例 一. 变量作用域 函数内部可以使用全局变量 函数外部不可以使用局部变量 当函数执行完毕时,本作用域内的局部变量会被销毁 二. 闭包 闭包:有权访问另一个函数作用域中 ...

  5. html5代码转换为视频,HTML5中的视频代码详解

    摘要 腾兴网为您分享:HTML5中的视频代码详解,智学网,云闪付,易推广,小红书等软件知识,以及360win10,流量魔盒,fitbit,上港商城,安卓2.3.7,全民惠,五年级下册英语单词表图片,t ...

  6. VMware虚拟机文件夹中各文件作用详解

    VMware虚拟机文件夹中各文件作用详解 虚拟机的文件管理由VMware Workstation来执行. 一个虚拟机一般以一系列文件的形式储存在宿主机中, 这些文件一般在由workstation为虚拟 ...

  7. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  8. php中 继承中的概念,JavaScript_JavaScript中的继承方式详解,js继承的概念 js里常用的如下 - phpStudy...

    JavaScript中的继承方式详解 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于 ...

  9. Java中的static关键字详解

    ** Java中的static关键字详解 ** 在一个类中定义一个方法为static,即静态的,那就是说无需本类的对象就可以调用此方法.调用一个静态方法就是 "类名.方法名" ,静 ...

最新文章

  1. flutter doctor --android-licenses 报错解决方案
  2. Centos 6.4下 MySQL配置主从服务(集群)
  3. 一个项目有两个pom_实现一个Spring Boot Starter超简单,读 Starter 源码也不在话下...
  4. httpclient妙用一 httpclient作为客户端调用soap webservice(转)
  5. 前端技术之_CSS详解第五天
  6. 13寸笔记本电脑尺寸_如何判断行李箱的尺寸?标准行李箱尺寸对照表(13~32寸)
  7. mysql添加用户权限报1064 - You have an error in your SQL syntax问题解决
  8. 万人报名2020腾讯广告算法大赛,顶级技术争锋正式开战!
  9. java 清单文件 生成,使用批处理文件生成文件列表清单
  10. linux使用fabric教程,Hyperledger fabric在Linux下的环境搭建
  11. oracle 测试坚挺,access数据库用户依然坚挺,但是面临新的对手挑战
  12. 2018科大讯飞AI营销算法大赛总结(冠军)
  13. 洛谷 P2241统计方形(数据加强版) 题解
  14. React脚手架搭建及创建React项目
  15. PS 在PS中如何等比例放大缩小图片
  16. Linux中存放用户密码信息的文件,存放密码过期修改等信息
  17. linux系统双显卡切换显卡驱动,Ubuntu 14.04 安装 Nvidia 私有驱动并进行双显卡切换...
  18. 实时数据库和关系数据库的区别
  19. “寻找下一代CTO”-- 机会啊
  20. C盘与E盘 磁盘目录结构

热门文章

  1. PHP中4个包含文件方法的差异
  2. 使用RabbitMQ实现延迟任务
  3. windows下mysql主从同步_详解windows下mysql的主从同步
  4. Zookeeper深入系列-从Zookeeper中深入JMX原理
  5. Redis数据结构——简单动态字符串-SDS
  6. Valgrind的使用方法
  7. Go 面试专题 | slice 扩容后的内存容量如何计算?
  8. @JsonFormat与@DateTimeFormat注解的使用
  9. Java 常用负载均衡算法解析
  10. 制作 docker 镜像