Swift函数和闭包

时间:2021-10-27 22:45:37
1、函数声明 使用 func 关键字来声明一个函数,使用名字和参数来调用函数,使用 -> 来指定函数返回值类型。
func greet(name:String, day:String) -> String {
    return "Hello \(name), today is \(day)"
}

//调用函数
greet("Andy", "Tuesday")
注:如果一个函数我们不需要返回值,则也就没必要使用 -> 指定返回值类型了。

2、函数返回多个值(需要使用元组tuple) 我们可以使用一个元组来返回多个值。 func getMultipleVal() -> (Double, Double, Double){
    return (3.56, 4.22, 5.34)
}

//获得元组的值
var (va1, va2, va3) = getMultipleVal()

3、外部形参名 有时当你调用一个函数将每个形参进行命名是非常有用的,以表明你把每个实参传递给函数的目的。 你写一个外部形参名称在它所支持的本地形参名称之前,之间用一个空格来分隔。 func someFunction(externalParameterName localParameterName: Int) {        ... } 如上,如果我们提供了外部形参名 externalParameterName , 则外部形参必须在调用时使用。
例如: func join(str1 s1: String, str2 s2: String, withJoiner joiner: String) -> String {        return s1 + joiner + s2  } join(str1: "hello", str2: "world", withJoiner: ", ") 
4、外部参数名速记 如果你想为一个函数提供一个外部形参名,然而本地形参名已经使用了一个合适的名称了,那你就不需要两次书写该形参的名称。相反,你可以写一次名字,并用一个hash符号(#)作为名称的前缀告诉SWIFT本地形参与外部形参名字相同。
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {        for character in string {            if character == characterToFind {                return true            }        }        return false  let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v") 
5、默认参数名 一般来说,存在默认值的参数放在后面,而且当一个参数有默认值,则SWIFT默认会生成一个与参数名相同的外部参数。 func join(s1: String, s2: String, joiner: String = " ") -> String {        return s1 + joiner + s2 
join("hello", "world") // 使用joiner默认值 join("hello", "world", joiner: "-")  //改变joiner默认值

6、可变参数 函数的参数数量是可变的,传递至可变形参的值在函数主体内是以适当类型的数组存在的。 使用可变参数只需要在第一个变量后面加上 ...
func sumOfNumber(numbers:Int...)->Int{
    var sum = 0
    for number in numbers{
        sum += number
    }
    return sum
}
注:函数最多可以有一个可变形参,而且它必须出现在参数列表的最后,以避免使用多个形参调用函数引发歧义。
7、变量形参默认函数的形参是一个常量形参,表示在函数的内部,我们不能改变这个参数的值。如果你想要改变,就必须使用变量形参。变量形参在参数名称前用关键字 var 定义。func alignRight(var string: String, count: Int, pad: Character) -> String {       let amountToPad = count - countElements(string)       for _ in 1...amountToPad {           string = pad + string       }       return string }let paddedString = alignRight(originalString, 10, "-")
注:在上面,一般来说参数 string 是一个常量,不能在函数内部进行值修改,现在我们使用了var 变量,使其成为了变量形参。
8、In-Out形参变量形参只能在函数本身内改变。如果你想让函数改变形参值,并想要在函数调用结束后保持形参值的改变,那你可以把形参定义为 in-out 形参。
通过在形参定义的开始添加 inout 关键字来编写 in-out 形参。当你把变量作为实参传递给 in-out 形参时,需要在在变量前添加 & 符号,以表明它可以被函数修改。例如:func swapTwoInts(inout a: Int, inout b: Int) {
    let temporaryA = a
    a=b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)”)
注:上面的例子,我们只是简单的交换函数的值,但是使用in-out参数后,执行完函数,则其变量的值也进行了改变
9、函数赋值给一个变量var mathFunction: (Int, Int) -> Int = addTwoInts可以解读为:"定义一个名为 mathFunction 变量,该变量的类型为'一个函数,它接受两个 Int 值,并返回一个 Int 值,'设置这个新的变量来引用名为 addTwoInts 函数。"
10、函数的特性(1)可以在其他函数体中定义函数,被称为嵌套函数。func makeIncrement() -> (Int)->Int {
    func addOne(number:Int)->Int{
        return 1+number
    }
   
    return addOne
}

注:上面是一个嵌套函数,并且他返回的还是一个函数类型。
(2)你可以将一个函数类型作为另一个函数的返回类型例如:我们定义两个函数表示前进和后退,他们都是返回一个Int类型的函数。func stepForward(input: Int) -> Int {
    return input + 1
}

func stepBackward(input: Int) -> Int {
    return input - 1
}


然后定义一个函数来表示选择前进还是后退,它的返回类型是"函数类型(Int) -> Int”。func chooseStep(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}


(3)函数也可以当做参数传入另一个函数。func addTwoInts(a: Int, b: Int) -> Int {
   return a + b
}

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int)
{
    println("Result: \(mathFunction(a, b))")
}

printMathResult(addTwoInts, 3, 5)


注:printMathResult 函数它有三个形参。第一个形参名为 mathFunction,类型为(Int, Int)->Int,您可以传递任何同类型的函数作为第一个形参的实参;第二和第三个参数 a、b 都是 int 类型,被用来作为数学函数的两个输入值

11、闭包闭包可以捕获和存储其所在上下文中任意常量和变量的引用,这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。
闭包采取如下三种形式之一:1. 全局函数是一个有名字但不会捕获任何值的闭包
2. 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
3. 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的没有名字的闭包



12、闭包表达式语法{ (parameters) -> returnType in       statements 注:闭包的函数体部分由关键字  in  引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
非闭包排序:我们使用 sort 函数来排序,非闭包方法是先定义一个函数,然后把这个函数作为参数传给 sort  .let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {      return s1 > s2 
var reversed = sort(names, backwards) 
注:如果第一个字符串 (s1) 大于第二个字符串 (s2),backwards 函数则返回  true,表示在新的数组中 s1 应该出现在 s2 前。 字符中的 "大于" 表示 "按照字母顺序后出现"。 
闭包排序:let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
reversed = sort(names, { (s1: String, s2: String) -> Bool in      return s1 > s2 }) 


13、根据上下文推断闭包类型如上,因为排序闭包是作为函数的参数进行传入的,Swift可以推断其参数和返回值的类型。sort 期望第二个参数是类型为  (String, String) ->  Bool 的函数,因此实际上  String, String 和 Bool 类型并不需要作为闭包表达式定义中的一部分。
reversed = sort(names, { s1, s2 in return s1 > s2 } )
注:但是我们鼓励用户写清楚类型,这样更加方便阅读。
14、单行表达式闭包可以省略 return单行表达式闭包可以通过隐藏 return 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
reversed = sort(names, { s1, s2 in s1 > s2 } )
因为sort 函数的第二个参数函数类型明确了闭包必须返回一个  Bool 类型值。因为闭包函数体只包含了一个单一表达式 (s1 > s2),该表达式返回 Bool 类型值,因此这里没有歧义,return关键字可以省略。

15、闭包参数名简写Swift  自动为内联函数提供了参数名称简写功能,您可以直接通过  $0,$1,$2等名字来引用的闭包的参数的值。如果您在闭包表达式中使用参数名称简写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称简写的类型会通过函数类型进行推断。  in 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:reversed = sort(names, { $0 > $1 } )

16、Trailing 闭包如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用  trailing 闭包来增强函数的可读性。Trailing 闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用 例如:在上例中作为 sort 函数参数的字符串排序闭包可以改写为Trailing 闭包。reversed = sort(names) { $0 > $1 }
17、Trailing 闭包实例Swift 的 Array 类型有一个  map 方法,其获取一个闭包表达式作为其唯一参数。  数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。
下例介绍了如何在  map 方法中使用  trailing 闭包将 Int 类型数组  [16,58,510] 转换为包含对应 String 类型的数组 ["OneSix", "FiveEight", "FiveOneZero"]:
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 strs = numbers.map{
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[ number % 10 ]! + output
        number /= 10
    }
   
    return output
}

注:需要注意的时调用 numbers.map 不需要在 map 后面包含任何括号,因为只需要传递闭包表达式这一个参数并且该闭包表达式参数通过 trailing 方式进行撰写。字典  digitNames 下标后跟着一个叹号 (!),因为字典下标返回一个可选值  (optional value),表明即使该  key 不存在也不会查找失败,在上例中,它保证了  number  %  10 可以总是作为一个  digitNames 字典的有效下标  key。
18、捕获(Caputure)Swift 最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
上面的例子中,内部函数 incrementor() 就捕获了外部函数的2个变量。 
incrementor 捕获了当前 runningTotal 变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当makeIncrementor 结束时候并不会消失,也保证了当下一次执行 incrementor 函数时, runningTotal 可以继续增加。

注意:Swift 会决定捕获引用还是拷贝值。 您不需要标注 amount 或者 runningTotal 来声明 在嵌入的incrementor 函数中的使用方式。 Swift 同时也处理 runingTotal 变量的内存 管理操作,如果不再被 incrementor 函数使用,则会被清除。
执行代码:let incrementByTen = makeIncrementor(forIncrement: 10)
//值为10
incrementByTen()
//值为20
incrementByTen()
//值为30
incrementByTen()


如果您创建了另一个 incrementor,其会有一个属于自己的独立的 runningTotal 变量的引用。
let incrementBySeven = makeIncrementor( forIncrement: 7 )
//值为7
incrementBySeven()
//值为14
incrementBySeven()


19、闭包是引用类型无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen 指向闭包的引用是一个常量,而并非闭包内容本身。

函数和闭包都是引用类型,这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包。

//例如把 incrementByTen 赋值给一个新的常量,意味着他们指向同一个引用let alsoIncrementByTen = incrementByTen
//值为40,因为上面已经使用了3次
alsoIncrementByTen()