前言
Swift中函数的使用是非常灵活的,比如,函数有自己的类型,可以当作参数、返回值,还有Swift中还引入了函数式编程。
一、函数类型
1、什么是函数类型
每个函数都有特定的函数类型,函数类型是有函数的参数和返回值共同组成的。
(1) func addTwoInts(a:Int,_b:Int)->Int{ return a+b } (2) fun printHelloWorld(){ } (3) fun mathFunction()->Int{ }
上面三个函数的类型分别是(Int,Int)->Int,()->void,()->Int
在Swift中我们可以把函数类型像其他的类型一样使用,比如我们可以把函数类型声明成var,let,可以把函数类型当作参数,当作返回值等。
func addTwolints(a:Int,b:Int)->Int{ return a+b } func subtractionFuction(a:Int,b:Int)->Int{ return a - b } let addFuction:(Int,Int)->Int = addTwolints var subFuction:(Int,Int)->Int = subtractionFuction subsection = addFuction//类型一样的才能这么赋值 let result:Int = subsection(5,5)
2、函数类型怎么使用范围
函数类型可以做常量、变量参数、返回值,下面有几个小例子可以很好的说明
(1)、函数类型做常量和变量
func addTwolints(a:Int,b:Int)->Int{ return a+b } func subtractionFuction(a:Int,b:Int)->Int{ return a - b } let addFuction:(Int,Int)->Int = addTwolints//定义一个(Int,Int)->Int类型的常量addFuction var subFuction:(Int,Int)->Int = subtractionFuction subsection = addFuction//类型一样的才能这么赋值 let result:Int = subsection(5,5)
(2)、函数类型做参数
函数有三个参数有一个(Int,Int)->Int类型的mathFunction,和两个Int类型
\* printMathResult()函数有三个参数 *\ func printMathResult(mathFunction:(Int,Int)->Int,a:Int,b:Int){ print("Result:\(math)") } printMathResult(addTwolints,3,5)
(3)、函数类型做返回值
同样贴出官方给出的例子
fun setpForward(a:Int)->Int{ return a + 1 } func setpBackward(b:Int)->Int{ return b - 1 } func chooseStepFuction(backwards:Bool)->(Int)->Int{ return backwards ? setpForward: setpBackward }
二、闭包
感觉Swift中的函数也很简单明了,好吧,这儿有两个字符串看看他们是什么?怎么会有这么奇葩的写法
(a)、var myLowerString:String = { "myLowerString" }() (b)、 var myMaxString:String{ return "myMaxString" }
看上去是不是不好明白,其实上面两个是一样的他们都是使用Swift中的闭包给字符串赋值,只是写法进化了,现在我们一步一步的让它退化
(1)、上面a式可以退化成这样:
var myLowerString:String = { return "myLowerString" }()
只是多了个return还是不好明白
再次退化可以写成这样:
var myLowerString2:String = { () in return "myLowerString" }()
目前这个样子是不是已经很接近Closures的标准写法了,换一个式子加两个参数就是标准写法:
var myLowerString3:String = { (s1,s2) in return s1 + s2 }("hello","world")
补上两个参数(a)式终于露出了本来面目,上面的(b)式经过这样一步退化就会变成(a)式:
var myMaxString:String={ return "myMaxString" }()
好吧,如果我把Closures认为也是函数的一种特殊形式,或者反之那么Swift中函数的灵活度我已经无力吐槽了,其实这只是刚刚开始下面函数式编程会使其更加灵活。
1、Closures的表达式
上面举了个闭包的例子,现在照着官方文档一步步学习,闭包是自包含代码块,可以在代码中传递和使用(如果认为它是函数这个也很好理解),当然在Objective-C中我们把这种写法称为blocks(代码块)。官方文档说闭包有三种形式:
(a)、全局函数:全局函数是一个有名字但不会捕获任何值的闭包
(b)、嵌套函数:是一个有名字并可以捕捉起封闭函数域内值的闭包
(c)、闭包表达式:闭包表达式是一个利用轻量级语法缩写的可以捕获其上下文变量或常量的匿名闭包
(1)、全局函数和嵌套函数
对于全局函数和嵌套函数官方文档是这么说的:
All of the functions you have encountered so far in this chapter have been examples of global functions, which are defined at a global scope. You can also define functions inside the bodies of other functions, known as nested functions.
虽然没有找出明确的说明但是我觉得函数就是闭包?
这儿贴出两个函数作为参考:
func sayHello(personName: String) -> String { let greeting = "Hello, " + personName + "!" return greeting }//这个全局函数 func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backwards ? stepBackward : stepForward }// 这个嵌套函数理解起来很容易
(2)、捕获值(Capturing Values)
在全局函数和嵌套函数中有出现了一个词“捕获值(Capturing Values)”,官方解释是这样的:闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值
,大致意思也就是说闭包可以在它被定义的上下文中捕捉和持有变量和常量。官方使用嵌套函数很好解释了这个概念
func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }
makeIncrementor函数需要输入Int类型的参数amount,返回一个 () -> Int类型的函数,里面定义个一个可变的runningTotal,嵌套了一个incrementor函数,不需要参数,返回Int类型。我们可以看到内嵌函数incrementor拷贝并持有了一份amount,引用并持有了runningTotal。这样闭包持有的值占用的内存在什么时候释放,就有闭包去管理了。(说到这儿插入一句题外话,由于闭包的这个(捕获值)特性,闭包中如果出现self,self就会被闭包持有,如果实例直接或者间接的引用闭包就会造成一个”完美的环“,self->闭包->self,这样循环引用就悄无声息的形成了)
执行下面的代码:
let incrementByTen = makeIncrementor(forIncrement: 10) print(incrementByTen())//10 print(incrementByTen())//20 print(incrementByTen())//30
(3)、闭包表达式
现在终于轮到主角闭包表达式登场了,闭包表达式:闭包表达式是一种利用简洁语法构建内联闭包的方式
官方说他们对Swift的闭包进行了下列优化
(a)、利用上下文推断参数和返回值类型 (b)、隐式返回单表达式闭包,即单表达式闭包可以省略return关键字 (c)、参数名称缩写 (d)、尾随(Trailing)闭包语法
上面这些就是闭包写法进化的依据。再用官方的例子吧,毕竟我是写不出也想不出比官方给的例子更能表达问题的例子了。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = names.sort(backwards)// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
sort函数可以根据传入的排序函数对数组进行排序,并返回重新排序的数组。
如果传入闭包就应该是这样首先贴出闭包语法的一般形式:
{ (parameters) -> returnType in statements }
var reversed = names.sort({ (s1: String, s2: String) -> Bool in return s1 > s2 })
这么写跟上面写有什么却别?其实没有区别,只不过是把有名字的闭包(backwards)换成没名字的闭包(闭包表达式)
上面说了闭包表达式有上下文推断功能(Inferring Type From Context),那么由于names是字符串类型的数组,所有s1,s2类型应该可以推断出来上面的闭包表达式就可以开始进化成这样:
var reversed = names.sort( { s1, s2 in return s1 > s2 } )
单表达式隐式返回(Implicit Return From Single-Expression Clossures)又是怎么回事?
简单的说就是如果in后面的表达式只有return这一行,那么就可以省略掉return就像下面这样
reversed = names.sort( { s1, s2 in s1 > s2 } )
参数名缩写(Shorthand Argument Names)这个技能,所谓参数名缩写就是把闭包中的参数名省掉,而是用$0,$1,$2有序的替代参数。这样说的话,上面的式子可以接着简写成这样:
reversed = names.sort( { $0 > $1 } )
如果你觉得这样还不够简练那我们还可以接着简写只保留一个运算符号,干脆参数都不要了:
reversed = names.sort(>)
所有技能都用上进化成这种程度已经算是终极进化了吧。
4、尾随闭包(Trailing Closures)
尾随闭包需要一个条件:闭包作为最后一个参数
,同时尾随闭包体现在函数的调用上.
下面的例子就是使用尾随闭包的对比:
``
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
print("someFunctionThatTakesAClosure")
}
//不使用函数闭包进行函数调用
someFunctionThatTakesAClosure { () -> Void in
//闭包主体部分
}
//使用函数闭包进行函数调用
someFunctionThatTakesAClosure(){
//闭包主体部分
}
如果使用上面sort函数进行句例子,那么sort函数应该是这样的
reversed = names.sort(){>}
官方又说了,如果这个函数的参数只有这么一个闭包的话那么有可以这样简写:
reversed = names.sort{>}
写到这儿我不得不吐槽了,搞这么灵活是故意折麽我们程序员吗?是不是有点怀念Object-C了? 为了安慰我们官方有举了个例子,告诉我们这么写的好处:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
print(strings)
上面的map函数是这样的,map方法获取一个闭包表达式作为唯一参数,闭包函数会对数组中的元素每个调用一次,并且返回该元素所映射的值,拘役的映射方法和返回值类型,由闭包决定,说白了,也就是,闭包方法处理数组中的每一个值,并返回处理后的结果,numbers经过[%10] = [6,8,0],对应的字符串是:Six,Eight,Zero,经过number /= 10处理 = [1,5,51]对应的字符串是One,Five,OneZero,所以strings = ["OneSix", "FiveEight", "FiveOneZero"]