版权声明:本文为博主原创文章,未经博主同意不得转载。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他爹都非常难一眼看出来了。

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

。看下他帖的代码:
swift开发学习笔记-闭包-LMLPHP
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函数
闭包是引用类型
swift开发学习笔记-闭包-LMLPHP
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

05-11 11:22