版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/jiangqq781931404/article/details/32913421

文章转自:http://www.lookswift.com

闭包是个自包括的,能够在代码中传递的“块”。

。好吧,不纠结定义,继续。

swift的闭包,有点像C和Objective-C语言里的
代码块 {……} 
闭包能够捕捉而且保存在它所被定义的那个东西的上下文中定义的常量和变量。
事实上,在上一个笔记——函数,中的全局函数和嵌套函数,全都是闭包的特殊形式。
全局函数——是一个带有函数名的。并不捕捉不论什么值的函数。

嵌套函数——是一个带有函数名的而且从它被定义的外层函数中捕捉数值的函数。
闭包表达式——是一个不带函数名的。使用一种轻便的语法书写方式,而且能够从它被定义的外层函数中捕捉数值的函数。


从上面三个类型的函数的描写叙述,能够看出,事实上所谓的闭包(闭包表达式)。就是一个不带函数名的嵌套函数。仅仅只是,他的书写方式有点特别罢了。


swift
的闭包,鼓舞我们把他写得短小而且希望没有废话的方式。。。

我们都懂的,这种所谓的静止小巧短小尽量没有废话的方式。有时候会让人发狂,这正是闭包,这个玩意的难于理解之处吧!
只是。没关系,官方给的文档,足以让我们从一个让正人类能读懂的函数開始。慢慢变成一个非人正人类也可能会抓狂的函数的特殊形式——闭包。


刚才说到闭包推荐我们把函数尽量优化到所谓的短小和没有废话。那么先看一下。哪些部分会被“优化”掉:
1.
能够依据上下文推理出来的參数 以及 函数的返回值类型
2.
仅仅有一条语句构成的简单表达式的”return"keyword
3.
变量的名字
4.
Trailing closure syntax (后关闭的语法: 这个东西,我没有找到适合的解释。我自己的理解它相似于仅仅在函数的结尾才有返回的方式。也就是在不论什么一个函数体内,不论什么一个分支都不会打断函数的运行,一直到函数的最后一行的一个return或者无返回值之类的)
在swift的基本库中,有一个叫作sort的函数,它能够给Array中的元素排序,然后返回一个与传入的Array一样类型和元素个数的新的Array。
sort的原型:
sort(array:
T[], pred: (T, T) -> Bool)  //这是个泛型, pred是它内部用的。不用管,总之,參数2是个函数
假设參数2是个函数,这个不理解的话。还是先回头去把函数那部分复习一遍吧
这样可能不太easy读,那么我把它变形一下:
sort(myArray:
Int[],  (Int, Int) -> Bool)  //这是个Int型的处理方式
(感谢
swift技术交流第一平台(355277)的群友 packy(974871365) 指出之前写的时候,sort的右括号被我弄丢了,如今改好了 )
sort的第一个參数是个Int型数组,
第二个參数是个函数,这个函数有两个參数,返回值是Bool型
当然我们也能够把原型中的T换成其他的类型。比方:
sort(myArray:
String[],  (String, String) -> Bool)
sort(myArray:
Double[], (Double, Double) -> Bool)
以此类推,随便换成我们想要的形式
(泛型的意义如此)

let
names = [“Chris”, “Alex”, “Ewa”, “Barry”, “Daniella”]

func
myCompareFunc (s1: String, s2: String) -> Bool {   // 这个函数,跟sort的參数2 函数原型一样
 
   return s1 > s2
}
var
reversedNames = sort(names, myCompareFunc)  //得到了一个字母序倒序的数组
到这里。应该都非常easy理解了,sort的第一个參数。是个String数组,
第二个參数是个有两个參数而且返回Bool型的函数

sort会把names数组中的元素,按着它已经写好了的算法,取出某两个下标的值,然后传入到第二个參数定义的那个函数中,而第二个參数的函数是我们写的。假设參数1>
參数2,就返回true,这里是字符串,返回的字母表中字母的顺序,b排在a后面,所以b > a是true

前面说了,闭包有点像C语言和Objective-C里面的“代码块”,
所以。闭包的形式是这种:

                                                     //代码块開始
 
   (參数) -> 返回值类型 in              //注意这个语法: in 之后就是函数体了
 
        函数体

                                                     //代码块结束
更前面的时候。已经说了,闭包没有函数名。。。所以上面根本就没有函数名。但他仍然是个函数。


我们用闭包的形式。来改写上面写过的sort的调用。以及
myCompareFunc的定义:

reversedNames
= sort(names,  //參数2在以下

                                                     //代码块開始
 
   (s1: String, s2: String) -> Bool in     //注意这个语法: in 之后就是函数体了
 
        return s1 > s2                         

                                                     //代码块结束
) //
sort调用结束
这里sort的第二个參数,已经被我们替换成了上面提到的闭包的形式了
如今,我把一些换行符和一些空格以及凝视删掉,仅仅是删换行和空格和凝视哦:
reversedNames
= sort(names, {(s1:String, s2:String)->Bool in return s1 > s2} )  //对照一下,我没有多删东西吧
由于,这个闭包中的全部的參数类型(与sort的第一个參数的基本类型同样)
以及返回值类型(sort要求第二个函数的返回值类型为Bool)。全都是能够判断出来的,所以,类型能够省略,于是上面的这一条调用。就变成了:

reversedNames
= sort(names, {s1, s2 in return s1 > s2} )  //我仅仅删了參数类型和返回值类型
在上面的红字部分。提到。闭包中,能够省略的东西。2.仅仅有一条语句构成的简单表达式的”return”keyword,
所以,我们继续把return这个keyword也删掉, sort的调用就变成了:
reversedNames
= sort(names, {s1, s2 in s1 > s1})
到这里,是不是sort已经变得不太easy读了。

。。但还没完:
对于inline的闭包,
swift还提供了Shorthand Argument Names,作为參数的简写。以省去參数名,  $0代表第一个參数,$1代表第二个參数, $2……. 甚至连 in 都能够省了, 于是 sort调用的新版本号:
reversedNames
= sort(names, {$0 > $1})

操作符函数
上面的sort调用已经够短了。可是对于操作符
> 来说,它须要的是两个參数,而且返回值为Bool型,这正好符合了sort函数第2个參数所要求的函数形式,于是,我们能够把 > 当作函数,直接放在sort的第2个參数位置:

reversedNames
= sort(names, >)          //人类已经无法阻止闭包的简化了......
关于操作符作为函数,假设并不了解操作符重载的话,确实不好理解,那么请问度娘:“操作符重载”
是什么吧
Trailing
Closures
假设,闭包表达式作为函数的最后一个參数的时候。闭包表达式又非常长,不能像上面写成那么短的形式的话,那么,能够把闭包表达式,写在函数调用的外面。也就是()的后面,或者是以下:
func
myCallClosure(closure: () -> ()) {          //參数是个函数。名字叫closure而已。


 
   closure()              //用參数的名字closure, 调用传入的函数
}
myCallClosure({}) 
        //我仅仅是放了个空闭包{}在这里
假设{}中的内容非常长,我们能够把{}放在()外面:
myCallClosure()
{

}
这个看起来非常像函数的定义了,但他不是,由于函数名前面没有
funckeyword。

如今。我们回来看看sort,他的函数定义,也是最后一个參数要求传入函数,所以,我们能够把刚刚的sort,变成:
reversedNames
= sort(names) { $0 > $1 }    //我认为,连closure他爹都非常难一眼看出来了。

。。
我记得前几天,有群友,帖了这样一段代码,问:这是什么意思。今天,我才知道他一定是没有好好读手冊!。!!

。看下他帖的代码:

file:///Users/vincentpeng/Library/Containers/com.evernote.Evernote/Data/Library/Application%20Support/Evernote/accounts/Evernote-China/69033022/content/p181/292793c1dcf38eaa3818f2f39bf0116c.jpeg
通过上面闭包的学习,如今是不是非常easy看懂这个代码就是个闭包了?
仅仅只是map后面没有写()
至于这个numbers.map是怎么用的(numbers是个Array,map是Array的方法),这段代码的具体意思。手冊上都有非常详尽的解说,我就不列在这里。也不要再问这个问题了,不要搞得好像自己学习能力非常差一样。

。。
Capturing
Values
早些时候,提到了闭包能够捕捉并保存它的外层函数的常量和变量,接下来。我们来看看,有什么奇妙的事情,假设上面的闭包内容都理解了之后,这一段,事实上没啥好说的,直接上官方代码:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VwZXJnZXJt/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" border="0" alt="" style="border:0px;" />
file:///Users/vincentpeng/Library/Containers/com.evernote.Evernote/Data/Library/Application%20Support/Evernote/accounts/Evernote-China/69033022/content/p181/bf34207ce7cd71a0629611ae948abf11.jpeg
incrementor是个嵌套函数,
它返回了一个Int。 可是奇妙的是,他没有传入參数。而是使用了一个叫作runningTotal的外层函数makeIncrementor定义的一个局部变量。当makeIncrementor返回incrementor函数的时候,实际上是返回了incrementor函数的一个复制出来的实体函数(每次调用makeIncrementor的时候都会复制一个新的incrementor函数), 正由于闭包能够保存它外层函数定义的常量和变量,所以,当外层函数的作用域已经不存在的时候,它依旧能够使用那个常量或变量的值:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VwZXJnZXJt/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" border="0" alt="" style="border:0px;" />
file:///Users/vincentpeng/Library/Containers/com.evernote.Evernote/Data/Library/Application%20Support/Evernote/accounts/Evernote-China/69033022/content/p181/75dee77bae22d9b325161d13255d9238.jpeg
看到每次调用
incrementByTen(),得到的结果在递增了吧? 

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VwZXJnZXJt/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" border="0" alt="" style="border:0px;" />
file:///Users/vincentpeng/Library/Containers/com.evernote.Evernote/Data/Library/Application%20Support/Evernote/accounts/Evernote-China/69033022/content/p181/0e4357bf6cb1d726aae8e1c620687005.jpeg
这一段,也证明了我上面的猜:每次调用makeIncrementor的时候都会复制一个新的incrementor函数
闭包是引用类型

file:///Users/vincentpeng/Library/Containers/com.evernote.Evernote/Data/Library/Application%20Support/Evernote/accounts/Evernote-China/69033022/content/p181/54946750d12121807df6b88b210523bd.jpeg
这里并没有调用
makeIncrementor去复制一个新的函数,而仅仅是定义了一个变量 alsoIncrementByTen 被赋值成了 incrementByTen //这个函数在上面的时候,已经被调用了多次,正由于闭包是引用类型,这里的alsoIncrementByTen实际上仅仅是 incrementByTen 引用的那个之前复制出来的函数的新引用而已,于是。得到的结果是50.
本文章来自:雨燕开发人员:http://www.lookswift.com

swift开发学习笔记-闭包的更多相关文章

  1. 【前端】移动端Web开发学习笔记【2】 & flex布局

    上一篇:移动端Web开发学习笔记[1] meta标签 width设置的是layout viewport 的宽度 initial-scale=1.0 自带 width=device-width 最佳实践 ...

  2. 【前端】移动端Web开发学习笔记【1】

    下一篇:移动端Web开发学习笔记[2] Part 1: 两篇重要的博客 有两篇翻译过来的博客值得一看: 两个viewport的故事(第一部分) 两个viewport的故事(第二部分) 这两篇博客探讨了 ...

  3. 驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址

    驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址 最近重新看了乾龙_Heron的<ARM 上电启动及 Uboot 代码分析>(下简称<代码分析>) ...

  4. android开发学习笔记000

    使用书籍:<疯狂android讲义>——李刚著,2011年7月出版 虽然现在已2014,可我挑来跳去,还是以这本书开始我的android之旅吧. “疯狂源自梦想,技术成就辉煌.” 让我这个 ...

  5. 高性能Cordova App开发学习笔记

    高性能Cordova App开发学习笔记 文件结构 添加插件 构建准备 各个www的作用,prepare命令会将hello\www的内容会拷贝到platform下的wwww目录,知道该改哪里了吧?如果 ...

  6. Rest API 开发 学习笔记(转)

    Rest API 开发 学习笔记 概述 REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表示方式.获得这些表徵致使这些应用程序转变了其状态.随着 ...

  7. 步步为营 SharePoint 开发学习笔记系列总结

    转:http://www.cnblogs.com/springyangwc/archive/2011/08/03/2126763.html 概要 为时20多天的sharepoint开发学习笔记系列终于 ...

  8. Kinect开发学习笔记之(一)Kinect介绍和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

  9. ASP.NET Core Web开发学习笔记-1介绍篇

    ASP.NET Core Web开发学习笔记-1介绍篇 给大家说声报歉,从2012年个人情感破裂的那一天,本人的51CTO,CnBlogs,Csdn,QQ,Weboo就再也没有更新过.踏实的生活(曾辞 ...

随机推荐

  1. ssh免秘钥登陆实现

    1.用处 搭建集群或者工作中登陆跳板机经常需要做免秘钥互相登陆彼此服务器. 2. 准备工作   假设A主机10.20.0.1想通过ssh登录到B主机10.20.0.2上.   那么客户端(A主机)需要 ...

  2. SELinux下更改mysql端口

    默认情况下 mysql更改端口后是不能通过selinux的 提示启动错误,那么首先就要看mysql的错误日志 可是我不知道mysql错误日志的位置 首先,更改selinux的模式为passive 然后 ...

  3. Jsp2.0自定义标签(第三天)——EL表达式的使用

    1.提出问题: 我们经常会看到这样的jsp页面代码: 浏览器显示: 为什么会在页面输出:Hello World  ,${per}究竟是如何找到“Hello World”的呢? 2.分析问题: 要想解决 ...

  4. How To Commit Just One Data Block Changes In Oracle Forms

    You have an Oracle Form in which you have multiple data blocks and requirement is to commit just one ...

  5. JS请求报错:Unexpected token T in JSON at position 0

    <?php /* 最近做一个ajax validate表单验证提交的代码,在ajax提交的时候 JS请求报错:Unexpected token T in JSON at position 0 描 ...

  6. 更改VS2010 工程名的方法

    哇~~~~~~~啦啦啦~~~~~~~~ 太开心了,通过不断的尝试,我终于知道怎么更改VS2010的工程名了!!! 下面分享给大家: 1.打开自己想要更改名字的工程,用ctrl+h在整个项目中把想更改的 ...

  7. 【温故知新】——BABYLON.js学习之路·前辈经验(一)

    前言:公司用BABYLON作为主要的前端引擎,同事们在长时间的项目实践中摸索到有关BABYLON的学习路径和问题解决方法,这里只作为温故知新. 一.快速学习BABYLON 1. 阅读Babylon[基 ...

  8. JAVA Eclipse如何安装Swing

    查看自己的Eclipse版本   打开WINDOWBUILDER的下载页面,找到自己对应版本的下载地址,注意只是一个下载地址,不是要下载东西 http://www.eclipse.org/window ...

  9. PHP 变量定义及使用

    php的变量前面必须有$符号,而且是解释型的弱类型语言,定义的时候不需要定义变量值的类型. $str="这是个变量"; 1.输出的时候可以用拼接字符串的方法 如:echo" ...

  10. Spring2.5学习3.2_编码剖析@Resource注解的实现原理

    首先看一下J2EE提供的@Resource注解:该注解默认安照名称进行装配,名称能够通过name属性进行指定, 假设没有指定name属性,当注解写在字段上时,默认取字段名进行依照名称查找,假设注解写在 ...