Swift学习笔记(三)函数和闭包

1. 函数
①函数定义
[c]
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
[/c]

※每个函数参数都有一个参数标签( argument label )以及一个参数名称( parameter name )。
※参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。
※参数名称在函数的实现中使用。
※默认情况下,函数参数使用参数名称来作为它们的参数标签。

②返回元组
下例中定义了一个名为 minMax(array:) 的函数,作用是在一个 Int 类型的数组中找出最小值与最大值。
[c]
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)
}
[/c]
③默认参数值
[c]
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// 如果你在调用时候不传第二个参数,parameterWithDefault 会值为 12 传入到函数体中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12
[/c]
④可变参数(…)
通过在变量类型名后面加入(…)的方式来定义可变参数。
[c]
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)
// 返回 3.0, 是这 5 个数的平均数。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。
[/c]
⑤输入输出参数(同C中的引用传递)
函数参数默认是常量。定义一个输入输出参数时,在参数定义前加 inout 关键字。
传参时加上 & 前缀
[c]
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印 "someInt is now 107, and anotherInt is now 3"
[/c]
⑥函数类型(同C中的函数指针)
[c]
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
//这两个函数的类型是 (Int, Int) -> Int
func printHelloWorld() {
print("hello, world")
}
//这个函数的类型是:() -> Void
var mathFunction: (Int, Int) -> Int = addTwoInts
//mathFunction指向addTwoInts,两者有同样的类型
[/c]
[c]
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// 打印 "Result: 8"
//函数类型当作参数类型
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
//函数类型当作返回类型
[/c]
2. 闭包(Closures)
①相关概念
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。
在函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:

全局函数是一个有名字但不会捕获任何值的闭包
嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

利用上下文推断参数和返回值类型
隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字
参数名称缩写
尾随闭包语法
②语法
[c]
{ (parameters) -> returnType in
statements
}
[/c]
※闭包表达式参数 可以是 in-out 参数,但不能设定默认值。
※也可以使用具名的可变参数,但必须是最后一个参数。
※元组也可以作为参数和返回值。
[c]
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
//参数和返回值类型都可以推断出来,可以简写为
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
//单表达式闭包隐式返回
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
//参数名称缩写
reversedNames = names.sorted(by: { $0 > $1 } )
//运算符方法
reversedNames = names.sorted(by: >)
//采用尾随闭包
reversedNames = names.sorted() { $0 > $1 }
//如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:
reversedNames = names.sorted { $0 > $1 }
[/c]
③尾随闭包
增强函数的可读性
[c]
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
// 闭包主体部分
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
[/c]
④值捕获
[c]
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 返回的值为7
incrementByTen()
// 返回的值为30
[/c]
⑤闭包是引用类型
⑥逃逸闭包(@escaping)
[c]
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}

//将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}

class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// 打印出 "200"

completionHandlers.first?()
print(instance.x)
// 打印出 "100"
[/c]
⑦自动闭包
我们经常会调用采用自动闭包的函数,但是很少去实现这样的函数。举个例子来说,assert(condition:message:file:line:) 函数接受自动闭包作为它的 condition 参数和 message 参数;它的 condition 参数仅会在 debug 模式下被求值,它的 message 参数仅当 condition 参数为 false 时被计算求值。
[c]
//自动闭包让你能够<span style="color: red">延迟求值</span>,因为直到你调用这个闭包,代码段才会被执行。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// 打印出 "5"

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出 "5"

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出 "4"
[/c]
将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
[c]
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// 打印出 "Now serving Alex!"
[/c]
通过将参数标记为 @autoclosure 来接收一个自动闭包。
[c]
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// 打印 "Now serving Ewa!"
[/c]
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。
[c]
// customersInLine i= ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// 打印 "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// 打印 "Now serving Barry!"
// 打印 "Now serving Daniella!"
[/c]