Swift 函数

前言

  • Swift 中函数是用来完成特定任务的独立代码块。

    • 本节所讲的函数概念是全局的,不隶属于某一个数据结构。
    • 没有 “拥有者” 的函数的作用域是全局的,除非重载,否则不可能定义两个一模一样的函数。
    • 在 Swift 语言中没有主函数。
  • 方法是拥有归属关系的函数。

    • 方法的 “拥有者” 可能是类,也可能是结构体等。
    • 方法的 “拥有者” 为方法提供了命名空间,所以在不同的类或者结构体中可以定义完全相同的方法。

1、函数的定义

  • 函数定义需要关键字 func,其一般格式为

    1
    2
    3
    4
    5
    6
    func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }
  • 参数

    • 在函数定义中,我们使用的参数叫形式参数,在调用时传入的参数是实际参数。
    • 参数名后面用冒号相连的是参数类型,在定义函数时,需要明确参数类型。
    • 你可以指定多个参数,参数间用 , 分割。
  • 返回类型

    • 返回值类型使用组合符号 -> 来指示。
    • 如果不需要返回值可以写成 -> Void(注意 V 要大写),或者直接省略 -> 语句。
    • 没有定义返回类型的函数默认会返回 Void
    • 如果 -> 后面是 Void,则函数体中不需要写 return 语句。
  • 函数调用

    • 函数调用时只要传入的参数与函数定义中的参数类型相同即可。
    • 在 Swift 3.0 之前调用函数时,函数的首个参数名是自动隐藏的。
    • 在 Swift 3.0 及之后调用函数时,函数的所有参数是同等地位的,首个参数不再默认隐藏。

2、函数类型

  • Swift 中函数本身也是一种类型,函数类型通常由函数的形参类型和返回值类型组成。

    • 例如,函数

      1
      func addString(s1: string, s2: string, s2: string) -> String { }
    • 的 函数类型 是

      1
      (String, String, String) -> String
    • 如果一个函数没有形参或返回值,那么这个函数的类型是 () -> ()

  • 可以像使用 Swift 语言中其他类型一样使用函数类型。

    • 例如可以定义一个函数常量或函数变量,并像一般数据类型指定初始值一样为他指定一个对应的函数。与其他类型一样,当你给函数赋一个变量或者常量时,你可以让 Swift 语言去推断函数的类型。

      1
      2
      3
      4
      5
      6
      7
      func addString(s1: string, s2: string, s2: string) -> String { }

      // 指定函数类型
      var addSome:(String, String, String) -> String = addString

      // 推断函数类型
      let addSome = addString
    • 你也可以将函数作为参数传递给其他函数,或者将函数类型当作返回类型。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      func plus(a: Int, b: Int) -> Int {
      return a + b
      }

      func mult(a: Int, b: Int) -> Int {
      return a * b
      }

      func someFunction(func: (Int, Int) -> Int, a: Int, b: Int) {
      let result = paraFunc(a, b)
      print("The result is \(result)")
      }
      1
      2
      someFunction(paraFunc: plus, a: 3, b: 4)     // The result is 7
      someFunction(paraFunc: mult, a: 3, b: 4) // The result is 12

3、函数参数

  • 在 Swift 语言中,函数的形参和返回值是非常具有灵活性的,在需要的时候,可以定义一个或者多个甚至选择性的省略。

3.1 外部形参

  • Swift 语言也能支持 OC 的函数参数标签模式,这种模式被称为外部形参。

    • 如果你为参数制定了外部形参名,那么在调用的时候就必须显式的使用。
    • 如果别人第一次阅读你的代码,使用外部形参名称可以使你要表达的意思更加明确,上下文更加清晰。
    • 在函数调用时,如果你不想显示形参名,可以使用下划线 _ 作为外部形参名。
  • 定义格式

    1
    2
    3
    4
    5
    6
    7
    // 使用时本地形参名(本地形参名1、本地形参名2, ...)会被省略
    func 函数名 (外部形参名1 本地形参名1: 参数类型, 外部形参名2 本地形参名2: 参数类型, ...) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }
  • 在写外部形参名时,完全可以只写一次名字,只需要用一个 hash 符号 # 作为参数名称的前缀,从而告诉 Swift 我们使用了名称相同的本地形参名称和外部形参名称。

  • 定义格式

    1
    2
    3
    4
    5
    6
    func 函数名 (#参数名1: 参数类型, #参数名2: 参数类型, ...) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }
    • 从 Swift 3.0 起不再支持该种定义方式。

3.2 默认值形参

  • 在 Swift 语言中可以为任何形参定义默认值以作为函数定义的一部分。

    • 如果已经定义了默认值,那么调用函数时就可以省略该形参。
    • 为了避免遗漏参数或者参数传递的二义性,需在函数形参列表的末尾放置带默认值的形参,不要在非默认值的形参前放置。
    • 在有定义默认值的情况下,当没有指定外部形参名称时,Swift 语言将为你定义的任何默认值形参提供一个自动外部形参名,这个自动外部形参名和本地形参名相同。
  • 定义格式

    1
    2
    3
    4
    5
    6
    func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 默认值形参名: 参数类型 = 默认值) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }

3.3 可变数量形参

  • 当不能确定调用函数需要传入具体参数的数量时,应使用可变形参。

  • 可变形参是指可接受零个或多个指定类型值的形参,可以用它来传递任意数量的输入参数。

    • 声明可变形参时需在参数类型后面使用 ...
    • 当参数传递进函数体后,参数在函数体内可以通过集合的形式访问。
    • 一个函数最多可以有一个可变参数,而且它必须出现在参数列表的最后。
  • 定义格式

    1
    2
    3
    4
    5
    6
    func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 可变形参名: 参数类型...) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }
  • 可变数量形参的使用

    1
    2
    3
    4
    5
    6
    7
    func arithmeticmean(numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
    total += number
    }
    return total / Double(numbers.count)
    }
    1
    2
    print(arithmeticmean(numbers: 1, 2, 3))              // 2.0
    print(arithmeticmean(numbers: 1, 2, 3, 4, 5)) // 3.0

3.4 可变值形参

  • Swift 语言函数的形参默认是常量,我们不能直接在函数体内部改变形参的值,也就是说函数的形参默认是值类型的。

  • 但是如果需要在函数体内部修改函数参数值,可以使用可变参数,

    • 要定义可变参数可以在参数名前使用 var 关键字。
    • 可变参数可以让你能够修改形参的值,它可以给函数体一个可修改的形参值副本。
    • 但这并不意味着可变形参就是引用类型的。
  • 虽然在 Swift 2.2 中仍旧可以使用这个语法,但是在 Swift 3.0 中这种写法已经被废弃了,如果需要修改参数的值,可以在函数体内创建新的成员变量。

  • 定义格式

    1
    2
    3
    4
    5
    6
    func 函数名 (var 参数名1: 参数类型, var 参数名2: 参数类型, ...) -> 函数返回类型 {

    函数体 .....

    return 返回值
    }

3.5 引用形参

  • 在实际的编码中,我们往往需要在函数体内部修改形参值,并同时作用到实参本身,从而省去增加返回值数量的步骤。

    • 这时可以把形参定义为 in-out(输入输出参数)类型。
    • Swift 3.0 之前,要定义 in-out 类型的参数,需要在参数名前使用 inout 关键字修饰。
    • Swift 3.0 及之后,要定义 in-out 类型的参数,需要在参数类型前使用 inout 关键字修饰。
    • 当把变量传递给 in-out 形参时,必须在变量前添加 “&” 符号,以表明他被函数内部修改的是它本身的引用。
  • 定义格式

    1
    2
    3
    4
    5
    // Swift 3.0 之前
    func 函数名 (inout 参数名1: 参数类型, 参数名2: 参数类型, ...) {

    函数体 .....
    }
    1
    2
    3
    4
    5
    // Swift 3.0 及之后
    func 函数名 (参数名1: inout 参数类型, 参数名2: 参数类型, ...) {

    函数体 .....
    }
  • 使用 in-out 参数的同时有几条规则需要注意

    • 只能输入一个变量作为输入输出参数,不能将常量和字面量传递进函数。
    • 不能同时将参数标记为 varletinout
    • 可变形参的参数不能标记为 inout
    • 函数不能有默认值。
  • 引用形参的使用

    1
    2
    3
    4
    func exampleOfFunction(parameter: inout String) -> String {
    parameter = "Hello \(parameter)"
    return parameter
    }
    1
    2
    3
    4
    var name = "xiaoming"

    print(exampleOfFunction(parameter: &name)) // Hello xiaoming
    print(name) // Hello xiaoming

4、嵌套函数

  • 在一个函数体中定义另外一个函数体就称为嵌套函数。

    • 嵌套的函数默认对外是隐藏的,但仍可以通过包裹他们的函数调用和使用它们。
  • 定义格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    func 函数名1 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型1 {

    func 函数名2 (参数名3: 参数类型, 参数名4: 参数类型, ...) -> 函数返回类型2 {

    函数体2 .....

    return 返回值2
    }

    函数体1 .....

    return 返回值1
    }

5、函数重载

  • 在 Swift 中函数重载十分常用。

    • 由于 Swift 是一门强类型语言,可以修改参数列表或者使用不同的返回值实现函数重载。
    • 另外 Swift 中有一些特殊的函数是以操作符的形式定义的,比如 ==,如果想让自己定义的数据结构可以使用某个操作符,那么可以对操作符进行重载。
  • 参数列表重载的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    func test(s: Int) -> Int {
    print("Int")
    return 1
    }

    // 修改参数名
    func test(a: Int) -> Int {
    print("String")
    return 2
    }

    // 修改参数类型
    func test(s: String) -> Int {
    print("String")
    return 3
    }
    1
    2
    3
    let intValue    = test(s: 0)         // 1
    let intValue = test(a: 0) // 2
    let stringValue = test(s: "") // 3
    • 在调用函数时,编译器会检查函数所处的 “上下文”,确定参数名和参数的类型,调用不同的实现。
  • 返回值重载的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func test(s: String) -> Int {
    print("Int")
    return 3
    }

    // 修改返回值类型
    func test(s: String) -> String {
    print("String")
    return s
    }
    1
    2
    let intValue   : Int    = test(s: "")       // 3
    let stringValue: String = test(s: "") // ""
    • 在调用函数时,编译器会检查函数所处的 “上下文”,确定返回值的类型,调用不同的实现。
    • 因为函数存在多种 “上下文”,因此类型推断不再适用了。
    • 如果写出下面的代码编译器会提示你明确 unKnow 的类型。

      1
      let unKnow = test(s: "")
  • 操作符重载的使用

    1
    2
    3
    4
    5
    6
    7
    struct BoxInt {
    var intValue: Int
    }

    func == (lhs: BoxInt, rhs: BoxInt) -> Bool {
    return lhs.intValue == rhs.intValue
    }
    1
    2
    3
    4
    let b1 = BoxInt(intValue: 1)
    let b2 = BoxInt(intValue: 2)

    print(b1 == b2) // false

5、函数风格

  • Swift 3.0 之后,函数风格发生了较大的变化,这主要是受 Swift 3.0 提出的 API 设计原则的影响。

  • 1)缩减函数名

    • 在 Swift 3.0 之前,Swift 的函数风格与 OC 类似,函数名需要承担描述首个参数的职责,在函数调用时,函数中首个参数的参数名默认会被隐藏,只在函数体中使用。
    • 从 Swift 3.0 开始,函数的首个参数名不再默认被隐藏,你可以把函数的方法名缩减,把关键字转移到首个参数的参数名上。

      1
      2
      3
      4
      5
      // Swift 3.0 之前
      arr.appendContentsof([4, 5])

      // Swift 3.0 及之后
      arr.append(contentsOf: [4, 5])
    • 这样无论是添加单个元素,还是添加整个数组的方法都统一为 append,API 更加整齐。

  • 2)用 “时态” 表示函数 “副作用”

    • 在通常的认知中,函数应该是 “无状态” 的,也就是说,无论调用多少次都能得到相同的结果,但是,有时候函数体内部也会修改函数外部的成员值。

      1
      2
      3
      4
      5
      6
      7
      var mutableArr = [1, 3, 2]

      // 在 Swift 3.0 之前,sort 方法叫做 sortInPlace
      mutableArr.sort()

      // sorted 是 sort 的副作用版本
      let immutableArr = mutableArr.sorted()
    • sort 是动词时态,代表这个方法没有副作用,也就是说,sort 方法只会对方法的调用者起作用,是一种原地排序。

    • sortedsort 方法的副作用版本,sorted 方法不会修改原数组,会把原数组拷贝后排序,并且返回排序好的数组,你可以把返回值理解成一种副作用。
  • 3)关键字可以用作参数名

    • 从 Swift 3.0 开始,大多数 Swift 关键字都可以被用作参数名。

      1
      2
      3
      4
      5
      6
      7
      8
      func index(for element: Int, in collection: [Int]) -> Int? {
      for (num, e) in collection.enumerated() {
      if e == element {
      return num
      }
      }
      return nil
      }
      1
      index(for: 3, in: [1, 2, 2, 2, 3])            // 4
    • for-in 关键字原本是用在循环中,在这里被用作函数的外部参数名。

文章目录
  1. 1. 前言
  2. 2. 1、函数的定义
  3. 3. 2、函数类型
  4. 4. 3、函数参数
    1. 4.1. 3.1 外部形参
    2. 4.2. 3.2 默认值形参
    3. 4.3. 3.3 可变数量形参
    4. 4.4. 3.4 可变值形参
    5. 4.5. 3.5 引用形参
  5. 5. 4、嵌套函数
  6. 6. 5、函数重载
  7. 7. 5、函数风格
隐藏目录