Swift 函数
Swift 函数用来完成特定任务的独立的代码块。
Swift使用一个统一的语法来表示简单的C语言风格的函数到复杂的Objective-C语言风格的方法。
-
函数声明: 告诉编译器函数的名字,返回类型及参数。
-
函数定义: 提供了函数的实体。
Swift 函数包含了参数类型及返回值类型:
函数定义
Swift 定义函数使用关键字 func。
定义函数的时候,可以指定一个或多个输入参数和一个返回值类型。
每个函数都有一个函数名来描述它的功能。通过函数名以及对应类型的参数值来调用这个函数。函数的参数传递的顺序必须与参数列表相同。
函数的实参传递的顺序必须与形参列表相同,-> 后定义函数的返回值类型。
语法
func funcname(形参) -> returntype { Statement1 Statement2 …… Statement N return parameters }
实例
以下我们定义了一个函数名为 runoob 的函数,形参的数据类型为 String,返回值也为 String:
import Cocoa func runoob(site: String) -> String { return (site) } print(runoob(site: "www.runoob.com"))
以上程序执行输出结果为:
www.runoob.com
函数调用
我们可以通过函数名以及对应类型的参数值来调用函数,函数的参数传递的顺序必须与参数列表相同。
以下我们定义了一个函数名为 runoob 的函数,形参 site 的数据类型为 String,之后我们调用函数传递的实参也必须 String 类型,实参传入函数体后,将直接返回,返回的数据类型为 String。
import Cocoa func runoob(site: String) -> String { return (site) } print(runoob(site: "www.runoob.com"))
以上程序执行输出结果为:
www.runoob.com
函数参数
函数可以接受一个或者多个参数,这些参数被包含在函数的括号之中,以逗号分隔。
以下实例向函数 runoob 传递站点名 name 和站点地址 site:
import Cocoa func runoob(name: String, site: String) -> String { return name + site } print(runoob(name: "菜鸟教程:", site: "www.runoob.com")) print(runoob(name: "Google:", site: "www.google.com"))
以上程序执行输出结果为:
菜鸟教程:www.runoob.com Google:www.google.com
不带参数函数
我们可以创建不带参数的函数。
语法:
func funcname() -> datatype { return datatype }
实例
import Cocoa func sitename() -> String { return "菜鸟教程" } print(sitename())
以上程序执行输出结果为:
菜鸟教程
元组作为函数返回值
函数返回值类型可以是字符串,整型,浮点型等。
元组与数组类似,不同的是,元组中的元素可以是任意类型,使用的是圆括号。
你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。
下面的这个例子中,定义了一个名为minMax(_:)的函数,作用是在一个Int数组中找出最小值与最大值。
import Cocoa func minMax(array: [Int]) -> (min: Int, max: Int) { var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) print("最小值为 \(bounds.min) ,最大值为 \(bounds.max)")
minMax(_:)函数返回一个包含两个Int值的元组,这些值被标记为min和max,以便查询函数的返回值时可以通过名字访问它们。
以上程序执行输出结果为:
最小值为 -6 ,最大值为 109
如果你不确定返回的元组一定不为nil,那么你可以返回一个可选的元组类型。
你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如(Int, Int)?或(String, Int, Bool)?
注意
可选元组类型如(Int, Int)?
与元组包含可选类型如(Int?, Int?)
是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
前面的minMax(_:)
函数返回了一个包含两个Int
值的元组。但是函数不会对传入的数组执行任何安全检查,如果array
参数是一个空数组,如上定义的minMax(_:)
在试图访问array[0]
时会触发一个运行时错误。
为了安全地处理这个"空数组"问题,将minMax(_:)
函数改写为使用可选元组返回类型,并且当数组为空时返回nil
:
import Cocoa func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { print("最小值为 \(bounds.min),最大值为 \(bounds.max)") }
以上程序执行输出结果为:
最小值为 -6,最大值为 109
没有返回值函数
下面是 runoob(_:) 函数的另一个版本,这个函数接收菜鸟教程官网网址参数,没有指定返回值类型,并直接输出 String 值,而不是返回它:
import Cocoa func runoob(site: String) { print("菜鸟教程官网:\(site)") } runoob(site: "http://www.runoob.com")
以上程序执行输出结果为:
菜鸟教程官网:http://www.runoob.com
函数参数名称
函数参数都有一个外部参数名和一个局部参数名。
局部参数名
局部参数名在函数的实现内部使用。
func sample(number: Int) { println(number) }
以上实例中 number 为局部参数名,只能在函数体内使用。
import Cocoa func sample(number: Int) { print(number) } sample(number: 1) sample(number: 2) sample(number: 3)
以上程序执行输出结果为:
1 2 3
外部参数名
你可以在局部参数名前指定外部参数名,中间以空格分隔,外部参数名用于在函数调用时传递给函数的参数。
如下你可以定义以下两个函数参数名并调用它:
import Cocoa func pow(firstArg a: Int, secondArg b: Int) -> Int { var res = a for _ in 1..<b { res = res * a } print(res) return res } pow(firstArg:5, secondArg:3)
以上程序执行输出结果为:
125
注意
如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
可变参数
可变参数可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数,其数量是不确定的。
可变参数通过在变量类型名后面加入(...)的方式来定义。
import Cocoa
func vari<N>(members: N...){
for i in members {
print(i)
}
}
vari(members: 4,3,5)
vari(members: 4.5, 3.1, 5.6)
vari(members: "Google", "Baidu", "Runoob")
以上程序执行输出结果为:
4 3 5 4.5 3.1 5.6 Google Baidu Runoob
常量,变量及 I/O 参数
一般默认在函数中定义的参数都是常量参数,也就是这个参数你只可以查询使用,不能改变它的值。
如果想要声明一个变量参数,可以在参数定义前加 inout 关键字,这样就可以改变这个参数的值了。
例如:
func getName(_ name: inout String).........
此时这个 name 值可以在函数中改变。
一般默认的参数传递都是传值调用的,而不是传引用。所以传入的参数在函数内改变,并不影响原来的那个参数。传入的只是这个参数的副本。
当传入的参数作为输入输出参数时,需要在参数名前加 & 符,表示这个值可以被函数修改。
实例
import Cocoa func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var x = 1 var y = 5 swapTwoInts(&x, &y) print("x 现在的值 \(x), y 现在的值 \(y)")
swapTwoInts(_:_:) 函数简单地交换 a 与 b 的值。该函数先将 a 的值存到一个临时常量 temporaryA 中,然后将 b 的值赋给 a,最后将 temporaryA 赋值给 b。
需要注意的是,someInt 和 anotherInt 在传入 swapTwoInts(_:_:) 函数前,都加了 & 的前缀。
以上程序执行输出结果为:
x 现在的值 5, y 现在的值 1
函数类型及使用
每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。
func inputs(no1: Int, no2: Int) -> Int { return no1/no2 }
inputs 函数类型有两个 Int 型的参数(no1、no2)并返回一个 Int 型的值。
实例如下:
import Cocoa func inputs(no1: Int, no2: Int) -> Int { return no1/no2 } print(inputs(no1: 20, no2: 10)) print(inputs(no1: 36, no2: 6))
以上程序执行输出结果为:
2 6
以上函数定义了两个 Int 参数类型,返回值也为 Int 类型。
接下来我们看下如下函数,函数定义了参数为 String 类型,返回值为 String 类型。
func inputstr(name: String) -> String { return name }
函数也可以定义一个没有参数,也没有返回值的函数,如下所示:
import Cocoa func inputstr() { print("菜鸟教程") print("www.runoob.com") } inputstr()
以上程序执行输出结果为:
菜鸟教程 www.runoob.com
使用函数类型
在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它:
var addition: (Int, Int) -> Int = sum
解析:
"定义一个叫做 addition
的变量,参数与返回值类型均是 Int
,并让这个新变量指向 sum
函数"。
sum
和 addition
有同样的类型,所以以上操作是合法的。
现在,你可以用 addition 来调用被赋值的函数了:
import Cocoa func sum(a: Int, b: Int) -> Int { return a + b } var addition: (Int, Int) -> Int = sum print("输出结果: \(addition(40, 89))")
以上程序执行输出结果为:
输出结果: 129
函数类型作为参数类型、函数类型作为返回类型
我们可以将函数作为参数传递给另外一个参数:
import Cocoa func sum(a: Int, b: Int) -> Int { return a + b } var addition: (Int, Int) -> Int = sum print("输出结果: \(addition(40, 89))") func another(addition: (Int, Int) -> Int, a: Int, b: Int) { print("输出结果: \(addition(a, b))") } another(addition: sum, a: 10, b: 20)
以上程序执行输出结果为:
输出结果: 129 输出结果: 30
函数嵌套
函数嵌套指的是函数内定义一个新的函数,外部的函数可以调用函数内定义的函数。
实例如下:
import Cocoa
func calcDecrement(forDecrement total: Int) -> () -> Int {
var overallDecrement = 0
func decrementer() -> Int {
overallDecrement -= total
return overallDecrement
}
return decrementer
}
let decrem = calcDecrement(forDecrement: 30)
print(decrem())
以上程序执行输出结果为:
-30
Swift 闭包
闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。
全局函数和嵌套函数其实就是特殊的闭包。
闭包的形式有:
全局函数 | 嵌套函数 | 闭包表达式 |
有名字但不能捕获任何值。 | 有名字,也能捕获封闭函数内的值。 | 无名闭包,使用轻量级语法,可以根据上下文环境捕获值。 |
Swift中的闭包有很多优化的地方:
语法
以下定义了一个接收参数并返回指定类型的闭包语法:
{(parameters) -> return type in
statements
}
实例
import Cocoa let studname = { print("Swift 闭包实例。") } studname()
以上程序执行输出结果为:
Swift 闭包实例。
以下闭包形式接收两个参数并返回布尔值:
{(Int, Int) -> Bool in Statement1 Statement 2 --- Statement n }
实例
import Cocoa let divide = {(val1: Int, val2: Int) -> Int in return val1 / val2 } let result = divide(200, 20) print (result)
以上程序执行输出结果为:
10
闭包表达式
闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
sorted 方法
Swift 标准库提供了名为 sorted(by:) 的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。
排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。
sorted(by:)方法需要传入两个参数:
实例
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] // 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。 func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = names.sorted(by: backwards) print(reversed)
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
如果第一个字符串 (s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 对于字符串中的字符来说,"大于" 表示 "按照字母顺序较晚出现"。 这意味着字母"B"大于字母"A",字符串"S"大于字符串"D"。 其将进行字母逆序排序,"AT"将会排在"AE"之前。
参数名称缩写
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
实例
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] var reversed = names.sorted( by: { $0 > $1 } ) print(reversed)
$0和$1表示闭包中第一个和第二个String类型的参数。
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
如果你在闭包表达式中使用参数名称缩写, 您可以在闭包参数列表中省略对其定义, 并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字同样也可以被省略.
运算符函数
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
Swift 的String
类型定义了关于大于号 (>
) 的字符串实现,其作为一个函数接受两个String
类型的参数并返回Bool
类型的值。 而这正好与sort(_:)
方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] var reversed = names.sorted(by: >) print(reversed)
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
尾随闭包
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure({ // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 }
实例
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] //尾随闭包 var reversed = names.sorted() { $0 > $1 } print(reversed)
sort() 后的 { $0 > $1} 为尾随闭包。
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
注意: 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把
()
省略掉。reversed = names.sorted { $0 > $1 }
捕获值
闭包可以在其定义的上下文中捕获常量或变量。
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
看这个例子:
func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }
一个函数makeIncrementor ,它有一个Int型的参数amout, 并且它有一个外部参数名字forIncremet,意味着你调用的时候,必须使用这个外部名字。返回值是一个()-> Int
的函数。
函数体内,声明了变量 runningTotal 和一个函数 incrementor。
incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。
由于没有修改amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。
所以我们调用这个函数时会累加:
import Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor } let incrementByTen = makeIncrementor(forIncrement: 10) // 返回的值为10 print(incrementByTen()) // 返回的值为20 print(incrementByTen()) // 返回的值为30 print(incrementByTen())
以上程序执行输出结果为:
10 20 30
闭包是引用类型
上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。
这是因为函数和闭包都是引用类型。
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
import Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor } let incrementByTen = makeIncrementor(forIncrement: 10) // 返回的值为10 incrementByTen() // 返回的值为20 incrementByTen() // 返回的值为30 incrementByTen() // 返回的值为40 incrementByTen() let alsoIncrementByTen = incrementByTen // 返回的值也为50 print(alsoIncrementByTen())
以上程序执行输出结果为:
50
Yeah
chr***@vip.com
19
闭包 可以类似 OC 里的 block 一样当作是一个对象,所以在最后一节“闭包是引用类型”中的例子,得到的一个闭包常量,这个闭包常量在内存里作为一个实例对象,此对象存储了捕获到的参数,调用三次就得到三个不同的结果:
typedef int(^xFuncTest) (int); -(xFuncTest)funcX { int __block inner = 0; xFuncTest blockTest = ^(int mark) { return inner += mark; }; return blockTest; } xFuncTest func = [self funcX]; NSLog(@"%d",func(1)); NSLog(@"%d",func(1)); NSLog(@"%d",func(1));