Swift 中的Closures(闭包)详解
Swift 中的Closures(闭包)详解
在Swift没有发布之前,所有人使用OC语言编写Cocoa上的程序,而其中经常被人们讨论的其中之一 -- Block 一直备受大家的喜爱。在Swift中,同样有这样的一个角色,用于当开发者需要异步执行的之后使用的一种语法 - Closure。中文翻译为闭包。
闭包出了可以进行异步执行之外,它的完整使用还依赖闭包本身的变量、常量的捕获。闭包捕获并存储对它们定义的上下文中的任何常量和变量的引用,这也就意味着,你可以在任何时候异步执行闭包的时候获取之前的所有的环境变量。而实际上,闭包类似于Swift 中的匿名函数,在上一篇文章中,介绍了高阶函数和嵌套函数,它们和闭包有者不可分割的一些联系。比如,最简单的闭包起始就是一个高阶函数,只是在闭包做为参数变量的时候,闭包是匿名、书写时实现。当对于一般的高阶函数,闭包更轻量级。
本文介绍几种闭包的形式,以及一些闭包的特性。
一、闭包的基本形式
这是一个最基本的闭包的形式:
{ (parameters) -> return type in
statements
}
闭包中,包含三要素: 参数,返回类型,闭包体。 其中参数和返回类型可以忽略, 但是一个闭包体必需存在,实际上就算在闭包体里面,什么都不写,闭包体本身还是以不执行任何代码的形式存在。
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return 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 = 0
func incrementer() -> Int {
runningTotal += amount
return 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 = 10
func 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: ))
// Prints "Now serving Ewa!"
在执行之前,customerProvider被当作一个 () -> String 处理,而不会涉及到闭包体内部的处理。 如果不使用@autoclosure,那么函数处理闭包体内的内容。
日常的开发可以使用:
public func Dlog( item:@autoclosure ()->Any){
#if DEBUG
print(item())
#else #endif
}
默认的自动闭包是 非逃逸闭包,如果想变成逃逸闭包,可以如下:
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
Swift 中的Closures(闭包)详解的更多相关文章
- [Swift实际操作]八、实用进阶-(9)Swift中的链表LinkedList详解
链表是一种物理存储单元上的非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的.相比于线性表的顺序结构,链表比较方便插入和删除操作.本文将讲解如何模拟一个链表. //链表的节点 ...
- Python闭包详解
Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...
- gradle中的build script详解
目录 简介 project和task 一个例子 task详细讲解 task脚本 task依赖 动态task 默认task build script的外部依赖 gradle中的build script详 ...
- C#中string.format用法详解
C#中string.format用法详解 本文实例总结了C#中string.format用法.分享给大家供大家参考.具体分析如下: String.Format 方法的几种定义: String.Form ...
- c++中vector的用法详解
c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间 ...
- 011-Scala中的apply实战详解
011-Scala中的apply实战详解 object中的apply方法 class中的apply方法 使用方法 apply方法可以应用在类或者Object对象中 class类 必须要创建实例化的类对 ...
- C# WinForm 中 MessageBox的使用详解
1.C# WinForm 中 MessageBox的使用详解:http://www.cnblogs.com/bq-blog/archive/2012/07/27/2611810.html
- JScript中的条件注释详解(转载自网络)
JScript中的条件注释详解-转载 这篇文章主要介绍了JScript中的条件注释详解,本文讲解了@cc_on.@if.@set.@_win32.@_win16.@_mac等条件注释语句及可用于条件编 ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
随机推荐
- Android实践 -- 监听应用程序的安装、卸载
监听应用程序的安装.卸载 在AndroidManifest.xml中注册一个静态广播,监听安装的广播 android.intent.action.PACKAGE_ADDED 监听程序卸载的广播 and ...
- js进阶 13-8 jquery如何实现侧边栏
js进阶 13-8 jquery如何实现侧边栏 一.总结 一句话总结:先是把侧边栏设置为left为-100px,隐藏起来,jquery自定义动画animate里面的改变元素的距左边的宽度left,sl ...
- Javascript和jquery事件-鼠标移入移出事件
javascript使用mouseover和mouseout,只在css中支持hover jquery支持mouseover和mouseout,封装了mouseenter.mouseleave事件函数 ...
- Android小经验
转载自:http://mp.weixin.qq.com/s?__biz=MzA4MjU5NTY0NA==&mid=404388098&idx=1&sn=8bbbba7692dc ...
- 9.5 Binder系统_驱动情景分析_transaction_stack机制
参考文章:http://www.cnblogs.com/samchen2009/p/3316001.html test_server服务进程可能有多个线程,而在发送数据的时候handle只表示了那个进 ...
- JVM学习:方法重载的优先级
重载:方法名一致,参数长度或者类型不一致. 先放总结,下面为例子 参数具有继承.实现关系,优先考虑子类: 在不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing),以及可变长 ...
- Spring Boot应用启动原理分析(转)
在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,不需要另外配置一个Web Server. 如果之前没有使用过sprin ...
- stm32的复用与映射
摘自:https://blog.csdn.net/lincheng15/article/details/51789093 摘自:http://www.51hei.com/bbs/dpj-36242-1 ...
- MySQLDump在使用之前一定要想到的事情
MySQLDump经常用于迁移数据和备份. 下面创建实验数据,两个数据库和若干表create database db1 ;use db1;create table t1(id int primary ...
- 【微信小程序】自定义模态框实例
原文链接:https://mp.weixin.qq.com/s/23wPVFUGY-lsTiQBtUdhXA 1 概述 由于官方API提供的显示模态弹窗,只能简单地显示文字内容,不能对对话框内容进行自 ...