Swift 中的Closures(闭包)详解
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(闭包)详解相关推荐
- Swift 中的内存管理详解
这篇文章是在阅读<The Swift Programming Language>Automatic Reference Counting(ARC,自动引用计数)一章时做的一些笔记,同时参考 ...
- JavaScript重难点解析4(作用域与作用域链、闭包详解)
JavaScript重难点解析4(作用域与作用域链.闭包详解) 作用域与作用域链 作用域 作用域与执行上下文 作用域链 闭包 闭包理解 将函数作为另一个函数的返回值 将函数作为实参传递给另一个函数调用 ...
- 【JavaScript 教程】ES6 中的 Promise对象 详解
[JavaScript 教程]ES6 中的 Promise对象 详解 1.Promise对象含义 promise是异步编程的一种解决方法. 所谓promise,简单说是一个容器,里面保存着某个未来才会 ...
- JavaScript闭包详解及案例
JavaScript闭包详解及案例 一. 变量作用域 函数内部可以使用全局变量 函数外部不可以使用局部变量 当函数执行完毕时,本作用域内的局部变量会被销毁 二. 闭包 闭包:有权访问另一个函数作用域中 ...
- html5代码转换为视频,HTML5中的视频代码详解
摘要 腾兴网为您分享:HTML5中的视频代码详解,智学网,云闪付,易推广,小红书等软件知识,以及360win10,流量魔盒,fitbit,上港商城,安卓2.3.7,全民惠,五年级下册英语单词表图片,t ...
- VMware虚拟机文件夹中各文件作用详解
VMware虚拟机文件夹中各文件作用详解 虚拟机的文件管理由VMware Workstation来执行. 一个虚拟机一般以一系列文件的形式储存在宿主机中, 这些文件一般在由workstation为虚拟 ...
- C++中substr()函数用法详解
C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...
- php中 继承中的概念,JavaScript_JavaScript中的继承方式详解,js继承的概念
js里常用的如下 - phpStudy...
JavaScript中的继承方式详解 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于 ...
- Java中的static关键字详解
** Java中的static关键字详解 ** 在一个类中定义一个方法为static,即静态的,那就是说无需本类的对象就可以调用此方法.调用一个静态方法就是 "类名.方法名" ,静 ...
最新文章
- flutter doctor --android-licenses 报错解决方案
- Centos 6.4下 MySQL配置主从服务(集群)
- 一个项目有两个pom_实现一个Spring Boot Starter超简单,读 Starter 源码也不在话下...
- httpclient妙用一 httpclient作为客户端调用soap webservice(转)
- 前端技术之_CSS详解第五天
- 13寸笔记本电脑尺寸_如何判断行李箱的尺寸?标准行李箱尺寸对照表(13~32寸)
- mysql添加用户权限报1064 - You have an error in your SQL syntax问题解决
- 万人报名2020腾讯广告算法大赛,顶级技术争锋正式开战!
- java 清单文件 生成,使用批处理文件生成文件列表清单
- linux使用fabric教程,Hyperledger fabric在Linux下的环境搭建
- oracle 测试坚挺,access数据库用户依然坚挺,但是面临新的对手挑战
- 2018科大讯飞AI营销算法大赛总结(冠军)
- 洛谷 P2241统计方形(数据加强版) 题解
- React脚手架搭建及创建React项目
- PS 在PS中如何等比例放大缩小图片
- Linux中存放用户密码信息的文件,存放密码过期修改等信息
- linux系统双显卡切换显卡驱动,Ubuntu 14.04 安装 Nvidia 私有驱动并进行双显卡切换...
- 实时数据库和关系数据库的区别
- “寻找下一代CTO”-- 机会啊
- C盘与E盘 磁盘目录结构