方法是执行特殊任务的自包含代码块。你可以给方法名字来表示它的功能,而且在需要的时候调用这个名字的方法来执行它的任务。
Swift方法的语法表达很灵活,从类似c的没有参数名的方法到oc复杂的带有名字和参数的方法。参数在简单方法的调用中可以提供默认的初始值,也可以传入一个变量的参数,当方法执行完后可以修改这个参数。
每个方法在Swift中都有一个类型,其中包括参数类型和返回类型。你可以享其他任何在Swift类型一样使用这些类型,它可以让方法更容易的为作为另外一个方法的一个参数值,从方法的返回值获取返回值。函数也可以写在其他的函数中,以便在嵌套的函数范围内封装有效的功能。
1.函数的声明与调用
当你定义一个函数的时候,你可能定义一个或者多个名字,函数的传入值的类型称为参数。你也可以选择定义一个方法执行后可能返回值的类行,称为返回值。
每个函数的名字最好能描述出它的功能。使用函数的时候,调用这个函数的名字并出入一个输入值(及我们所称的参数),这个参数的类型要与这个函数的参数类型一致。函数的参数必须始终以与函数的参数列表相同的顺序提供。
下面的例子定义了一个greet(person:),该函数输入一个人的名字,返回一个对这个人的问候字符串。为了实现这个例子,可以定义输入的参数,参数名为person、类型为String;以及返回一个String类型的返回值。
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
定义一个函数时把所有这些都组合一起,其中前缀用func 关键字。你定义函数返回值时用return的箭头“->”,后面跟着类型的名字返回。
这个函数的定义描述了它的功能是什么,接收什么参数,以及函数执行后返回的参数。这个定义可以让你在代码中任何地方更容易清楚的调用。
2.函数的参数与返回值
Swit函数中的参数和返回值是相当灵活的。你可以用从一个功能函数带一个没有命名的参数到 带有表达函数的参数和不同可选的参数的复杂函数定义一切。
(1)没有参数的函数
函数不需要定义输入参数。如下的这个函数没有带参数,每次调用该函数的时候,返回相同的字符串信息。
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
函数定义的时候仍然需要在函数名后面加上括号,尽管括号里面并没有参数。当函数调用的时候,函数名后面把空括号写上。
(2)函数带多个参数
函数可以带有多个输入参数,写在括号内,用“,”号分隔开。
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return greetAgain(person: person)
} else {
return greet(person: person)
}
}
print(greet(person: "Tim", alreadyGreeted: true))
// Prints "Hello again, Tim!"
(3)函数不带返回值
函数不需要定义一个返回类型。
func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"
因为不需要返回一个值,这个函数在定义的时候不包含返回箭头(->)或者返回值。
注意:严格的说,greet(person: String)的函数也有返回值,尽管它的返回并没有定义。函数没有定义返回值的类型时通常返回的是一个特殊类型值Void。它是一个简单的空元组,可以用“()”表示。
调用一个函数的时候,它的返回值可以忽略。
func printAndCount(string: String) -> Int {
print(string)
return string.count
}
func printWithoutCounting(string: String) {
let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting(string: "hello, world")
// prints "hello, world" but does not return a value
第一个方法 printAndCount(string:,打印一个字符串,然后返回Int类型的字符串个数。第二个方法
printWithoutCounting(string:)调用了第一个方法,但是忽略的它的返回值。当调用第二个函数的时候,第一个函数的信息依然打印,但是它的返回值并没有用到。
(4)函数带有多个返回值
你可以用元组的返回类型作为一个函数的多个返回值。
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)
}
minMax(array:)这个函数返回一个带有两个Int值的元组。
因为元组中的成员值被命名为函数返回值的一部分,它们可以通过点语法来获取到最小和最大值。
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"
注意:tuple的成员不需要在tuple从函数返回的时候被命名,因为它们的名称已经被指定为函数的返回类型的一部分。
(5)可选的元组返回值
如果从函数返回一个元组类型有可能是一个没有值的元组类型,你可以用一个可选的元组返回类型去表明这个元组可能为nil。你写一个可选的元组返回类型通过在元组类型的括号外加上一个“?”号,例如: (Int, Int)?或者(String, Int, Bool)?。
注意:一个可选的元组类型例如(Int,Int)?不同于(Int?, Int?)。对于可选元组类型,是整个元组类型 可选,不是元组内的每一个值可选。
为了解决空数组的安全,编写一个minMax(array:)函数,这个函数有一个可选元组返回值,当数组为空时,这个返回值为nil。
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)
}
你可以用可选值的绑定去检查 minMax(array:)这个方法的返回值是真的一个值还是一个nil。
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "min is -6 and max is 109"
3.函数的参数标签和参数名称
每个函数的参数都有参数的标签和参数名称。参数标签在函数调用时很有用的。在函数的实现用方法名会用得到。通常,参数用它们的参数名作为它们参数标签。
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
}
someFunction(firstParameterName: 1, secondParameterName: 2)
所有的参数必须用不一样的名字。尽管,多个参数可能会有相同的参数标签,唯一的参数标签可以帮助你的代码更加易读。
(1)指定参数标签
你可以在参数名字面前写上参数标签,用 空格分隔开。
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
当下是一个关于greet:(person:)的方法,输入一个人的名字还有家乡,返回一个问候语:
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino."
标签的使用使得函数像表达式一样调用,类似于句子,同时还使得函数体更容易解读、更清晰。
(2)省略参数标签
如果你的参数不需要参数标签,用下划线代替参数的参数标签。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
}
someFunction(1, secondParameterName: 2)
如果一个参数有参数标签,当你调用函数的时候,参数必须被标记。
(3)默认参数值
你可以在函数中为任何的参数定义一个默认的值,通过在参数类型的后面为参数分配一个值。如果你设置了默认的值,当你调用函数的时候,可以省略参数。
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// If you omit the second argument when calling this function, then
// the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
在函数的参数列表开始时,在具有默认值的参数之前,放置没有默认值的参数。没有默认值的参数通常对函数的意义更为重要——首先编写它们使识别相同的函数被调用变得更加容易,无论是否省略了默认参数。
(4)可变参数
一个可变的参数可以结束从0到其他更多指定类型的值。当函数调用的时候,你可以用可变参数指定可以输入参数的值。可变参数的实现是通过在参数名的后面插入三个省略号“...”。
传入可变参数的值在函数是有效地在函数体中以数组的形式存在。例如,一个可变参数名为numbers、类型为Double...在函数体作为一个名叫做numbers类型为[Double]数组常量存在的。
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
注意:一个函数可能最多有一个可变参数。
(5)输入输出参数
函数的参数值通常都是默认的。尝试在函数体的内部改变一个函数的参数值会出现编译时错误。这就是意味着你不能错误地改变一个参数的值。如果你想要修改一个函数的参数值,而且在函数调用结束后仍然保持这个改变值,定义这个参数为in-out参数来替代。
你可以用在in-out参数中传入一个变量,你不能传入常量作为参数,因为常量是不可更改的。当你给in-out参数传入一个参数值时,在变量名前面用&符号,表明这个变量在函数中是可改变的。
注意:输入输出参数不能有默认值,可变参数不能标记为输入输出参数。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
这个函数是两个值的交换,当你调用swapTwoInts这个方法是,传入2个Int类型的变量。当someInt和anotherInt值传入swapTwoInts时,前面带上&符号。
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
注意:输入输出参数与函数定义的返回值时不同的。swapTwoInts的例子中没有定义一个返回值和返回类型,但它仍然修改了someInt和anotherInt的值。输入输出参数为函数在函数体的范围内改变输出值提供了另外一种选择。
5.函数类型
每个函数都有一个特定的函数类型,由参数类型和函数返回值类型组成。
例如:
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
这个例子定义了两个简单的算术方法分别是addTwoInts和multiplyTwoInts。这两个函数都有两个Int的参数和返回一个Int类型的值,这个返回值算术的返回值。
这两个函数的类型都是 (Int, Int) -> Int。可以解读如下:
“一个方法有两个Int类型的返回值,返回一个Int类型的值”。
另外一个例子就是,一个函数没有参数和返回值:
func printHelloWorld() {
print("hello, world")
}
这个函数的类型是:()->Void 或者一个方法没有参数,返回值为void。
(1)用函数类型
正如使用Swift中其他类型一样使用函数类型。例如,你可以定义一个常量或者变量为函数类型,并为这个变量分配一个合适的函数。
var mathFunction: (Int, Int) -> Int = addTwoInts
可以解读如下:
定义一个叫做 mathFunction 的变量,这个变量是 带有两个Int参数的函数并返回一个Int值的类型。让这个变量指向addTwoInts的函数。
你可以用mathFunction调用分配的函数。
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
不同的函数有相同的类型就可以分配给相同的变量,对于没有函数类型的也一样:
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
与其他类型类似,你可以让Swift推断函数类型,当你把一个函数复制给一个常量或者变量的时候。
let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int
(2)函数类型作为参数类型
你可以用函数类型例如(Int, Int)->Int 作为另外一个函数的参数类型。这使您能够在函数调用时为函数的调用方保留函数实现的某些方面。
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"
这个叫做printMathResult方法的例子,有是三个参数。第一个参数叫做mathFunction,它的类型是(Int, Int)->Int。你可以传入任何符合这个函数类型的函数。第二和第三个参数分别是a和b,两个的类型都是Int类型。
printMathResult(_:_:_)的作用是将调用的结果打印成合适类型的数学函数。函数的实现实际上并不重要——它只关系到函数的正确类型。这使printMathResult(_:_:_:)能够以类型安全的方式将其功能传递给调用者。
(3)函数类型作为返回值类型
你可以用一个函数类型作为另外一个函数的返回值类型。在一个函数的返回箭头(->)后面加上函数类型来实现。
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
下面这个方法叫做 chooseStepFunction(backward:),它的返回类型是
(Int) -> Int。chooseStepFunction(backward:)函数根据backward的布尔值来判断是返回stepBackward函数还是返回stepForward函数。
6.嵌套函数
目前在本章节你所遇到的函数都是全部函数,它定义于全局范围。你可以在函数体中定义函数,也就是我们所说的嵌套函数。
默认情况下,嵌套函数隐藏在外部世界中,但仍然可以通过其封闭函数调用和使用。一个封闭函数还可以返回它的一个嵌套函数,允许在另一个范围内使用嵌套函数。
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!