6. Swift 函数 Function.png

函数与方法

函数与方法本质上没啥区别。如果非要说区别,那么函数是独立存在的,而方法存在于结构体、类中。

定义函数

函数表达式:

  1. func 函数名(外部参数名 参数名: 参数类型, ...) -> 返回值类型 {
  2. // 函数体
  3. }

注意:同一个类、结构体、协议内,如果函数名外部参数名参数名参数类型参数顺序返回值类型(函数头、函数声明均完全一致,那么这两个方法才算是同一个函数

无返回值的写法:

  1. func test() {}
  2. func test() -> Void {}

函数参数

支持函数参数设置外部参数名、缺省外部参数名、参数默认值、可变参数

设置外部参数名

  1. func create(your_name name: String, age: Int?) {
  2. print(name)
  3. }
  4. create(your_name: "huangjian", age: nil)

上面第一个参数中,your_name 作为外部参数名,供外部标签化调用,而 name 作为内部参数名。函数内部只能使用内部参数名,不能使用外部参数名

设置缺省外部参数名

  1. func create(_ name: String, age: Int?) {
  2. }
  3. create("huangjian", age: nil)

上面第一个参数中,使用了缺省外部参数名 _ ,意味着:外部调用时可不指明参数名

设置参数默认值

  1. func create(name: String, age: Int = 0, email: String) {
  2. }
  3. create(name: "huangjian", email: "mr.huangjian@foxmail.com")
  4. create(name: "huangjian", age: 20, email: "mr.huangjian@foxmail.com")

上面第二个参数中,设置了默认值为 0,意味着:外部调用时可以省略该参数的赋值

设置可变参数

  1. func create(name: String, args: Any...) {
  2. print(args)
  3. }
  4. create(name: "huangjian", args: 20, "mr.huangjian@foxmail.com")
  5. // [20, "mr.huangjian@foxmail.com"]

上面第二个参数为可变参数,它只能放在所有参数的最后一个,用 … 标识,指定它的参数类型(String / Int / Any…),并且可变参数是一个数组

函数的作用

函数除了给实例调用外,还可以作为一种类型、参数、返回值来使用,也就是闭包

函数作为类型

  1. /**
  2. 定义类型别名:
  3. typealias aliasType = Type
  4. 例子:
  5. typealias JSON = Dictionary
  6. */
  7. typealias dispatch_block_t = () -> Void
  1. // 下面两种写法等同
  2. var dispatch_block_t: (() -> ())
  3. var dispatch_block_t: (() -> Void)
  1. func test() {
  2. }
  3. dispatch_block_t = test
  1. dispatch_block_t = { () in
  2. }

作为类型不能使用标签化参数名,只能使用缺省参数名 _

  1. var block: ((String, Int, Any) -> String)
  2. var block: ((_ name: String, _ age: Int, _: Any) -> String)

函数作为参数

  1. func test(_ x: Int, _ y: Int, fn: (_ a: Int, _ b: Int) -> Int) {
  2. print(fn(x, y))
  3. }
  4. func add(a: Int, b: Int) -> Int {
  5. return a + b
  6. }
  7. func multi(a: Int, b: Int) -> Int {
  8. return a * b
  9. }
  10. test(1, 2, fn: add)
  11. test(1, 2, fn: multi)
  1. func test(_ x: Int, _ y: Int, fn: (_ a: Int, _ b: Int) -> Int) {
  2. print(fn(x, y))
  3. }
  4. test(1, 2, fn: { (a, b) -> Int in
  5. return a + b
  6. })
  7. test(1, 2, fn: { (a, b) -> Int in
  8. return a * b
  9. })
  10. // 3
  11. // 2

函数作为返回值

  1. func makePlus(forPlus amount: Int) -> () -> Int {
  2. var total = 0
  3. func plus() -> Int {
  4. total += amount
  5. return total
  6. }
  7. return plus
  8. }
  9. let block = makePlus(forPlus: 10)
  10. print(block()) // 10
  11. print(block()) // 20
  12. print(block()) // 30

其他用法

修改外部变量的值

参数默认是常量,所以不能在函数内修改参数的值,更不能修改传递过来的外部变量的值。(值类型)
可以在参数的类型前面加 inout 关键字,表示内部会修改改变外部的变量,调用时要加 & 符号。

  1. func swap( a: inout Int, b: inout Int) {
  2. let tt = a
  3. a = b
  4. b = tt
  5. }
  6. var a = 1, b = 2
  7. swap(a: &a, b: &b)
  8. print(a, b)
  9. // 2 1

函数结束前调用

  • defer 修饰函数内任一段代码块,代码块将在函数即将结束前调用
  • 如果定义了多个 defer 代码块,程序会从下往上的顺序执行 defer 代码块
  • 异步代码的执行,不会影响 defer 的执行时间
  1. func test(){
  2. print("函数开始了")
  3. defer {
  4. print("执行 defer1")
  5. }
  6. print("函数执行中")
  7. defer {
  8. print("执行 defer2")
  9. }
  10. print("函数将结束")
  11. }
  1. test()
  2. /**
  3. 函数开始了
  4. 函数执行中
  5. 函数将结束
  6. 执行 defer2
  7. 执行 defer1
  8. */

处理错误异常

try 手动 do-catch 捕捉异常
try? 如果该方法出现了异常,则该方法返回 nil,正常则返回对应的对象
try! 直接强制执行方法。如果该方法出现了异常,那么程序会崩溃

在可能出现异常的函数后面添加 throws,在调用的时候使用 do-catch,保证调用者对相应的错误进行处理。

throws 的使用很简单,只需要在可能出现异常的函数或者方法后面添加 throws。经过这个关键字修饰的函数,在调用的时候,需要程序员加上 do-catch 来调用。只需要使用 throws 进行修饰,就保证了以后的调用者必然需要对相应的错误进行处理。当然也可以不处理,但无论如何,错误被 throws 携带出来了,以后的维护和优化不需要重新做错误处理的设计,直接加上错误处理的逻辑即可。

Swift 中提供了 Error 协议,我们在开发中,如果要自定义自己的错误类型,一般会使用一个 Enum 来遵循 Error 协议,目的是享用 Error 已经包含的一些特性。

  1. enum MyError: Error {
  2. case ErrorOne
  3. }
  4. func willThrow(_ type: Int) throws -> String {
  5. if type == 1 {
  6. throw MyError.ErrorOne
  7. }
  8. return "That's OK"
  9. }
  10. do {
  11. let str = try willThrow(1)
  12. print(str)
  13. } catch let error as MyError {
  14. print("throws: ", error)
  15. } catch {
  16. }

rethrows 针对的不是函数或者方法本身,而是他携带的闭包类型的参数,当他的闭包类型的参数 throws 的时候,我们要使用 rethrows 将这个异常向上传递。

  1. enum MyError : Error {
  2. case ErrorOne
  3. }
  4. func willThrow(_ type: Int) throws -> String {
  5. if type == 1 {
  6. throw MyError.ErrorOne
  7. }
  8. return "That's OK"
  9. }
  10. func willRethrow(_ throwCall: (Int) throws -> String) rethrows {
  11. do {
  12. let result = try throwCall(1)
  13. print(result)
  14. } catch let error as MyError {
  15. throw error // 这里再次 throw
  16. } catch {
  17. }
  18. }
  19. do {
  20. try willRethrow(willThrow)
  21. } catch let error as MyError {
  22. print("rethorws: ", error)
  23. } catch {
  24. }

备注:[函数中的一些关键字]