
函数与方法
函数与方法本质上没啥区别。如果非要说区别,那么函数是独立存在的,而方法存在于结构体、类中。
定义函数
函数表达式:
func 函数名(外部参数名 参数名: 参数类型, ...) -> 返回值类型 {// 函数体}
注意:同一个类、结构体、协议内,如果函数名、外部参数名、参数名、参数类型、参数顺序、返回值类型(函数头、函数声明)均完全一致,那么这两个方法才算是同一个函数
无返回值的写法:
func test() {}func test() -> Void {}
函数参数
支持函数参数设置外部参数名、缺省外部参数名、参数默认值、可变参数
设置外部参数名
func create(your_name name: String, age: Int?) {print(name)}create(your_name: "huangjian", age: nil)
上面第一个参数中,your_name 作为外部参数名,供外部标签化调用,而 name 作为内部参数名。函数内部只能使用内部参数名,不能使用外部参数名
设置缺省外部参数名
func create(_ name: String, age: Int?) {}create("huangjian", age: nil)
上面第一个参数中,使用了缺省外部参数名 _ ,意味着:外部调用时可不指明参数名
设置参数默认值
func create(name: String, age: Int = 0, email: String) {}create(name: "huangjian", email: "mr.huangjian@foxmail.com")create(name: "huangjian", age: 20, email: "mr.huangjian@foxmail.com")
上面第二个参数中,设置了默认值为 0,意味着:外部调用时可以省略该参数的赋值
设置可变参数
func create(name: String, args: Any...) {print(args)}create(name: "huangjian", args: 20, "mr.huangjian@foxmail.com")// [20, "mr.huangjian@foxmail.com"]
上面第二个参数为可变参数,它只能放在所有参数的最后一个,用 … 标识,指定它的参数类型(String / Int / Any…),并且可变参数是一个数组
函数的作用
函数除了给实例调用外,还可以作为一种类型、参数、返回值来使用,也就是闭包
函数作为类型
/**定义类型别名:typealias aliasType = Type例子:typealias JSON = Dictionary*/typealias dispatch_block_t = () -> Void
// 下面两种写法等同var dispatch_block_t: (() -> ())var dispatch_block_t: (() -> Void)
func test() {}dispatch_block_t = test
dispatch_block_t = { () in}
作为类型不能使用标签化参数名,只能使用缺省参数名 _
var block: ((String, Int, Any) -> String)var block: ((_ name: String, _ age: Int, _: Any) -> String)
函数作为参数
func test(_ x: Int, _ y: Int, fn: (_ a: Int, _ b: Int) -> Int) {print(fn(x, y))}func add(a: Int, b: Int) -> Int {return a + b}func multi(a: Int, b: Int) -> Int {return a * b}test(1, 2, fn: add)test(1, 2, fn: multi)
func test(_ x: Int, _ y: Int, fn: (_ a: Int, _ b: Int) -> Int) {print(fn(x, y))}test(1, 2, fn: { (a, b) -> Int inreturn a + b})test(1, 2, fn: { (a, b) -> Int inreturn a * b})// 3// 2
函数作为返回值
func makePlus(forPlus amount: Int) -> () -> Int {var total = 0func plus() -> Int {total += amountreturn total}return plus}let block = makePlus(forPlus: 10)print(block()) // 10print(block()) // 20print(block()) // 30
其他用法
修改外部变量的值
参数默认是常量,所以不能在函数内修改参数的值,更不能修改传递过来的外部变量的值。(值类型)
可以在参数的类型前面加 inout 关键字,表示内部会修改改变外部的变量,调用时要加 & 符号。
func swap( a: inout Int, b: inout Int) {let tt = aa = bb = tt}var a = 1, b = 2swap(a: &a, b: &b)print(a, b)// 2 1
函数结束前调用
defer修饰函数内任一段代码块,代码块将在函数即将结束前调用- 如果定义了多个
defer代码块,程序会从下往上的顺序执行defer代码块 - 异步代码的执行,不会影响
defer的执行时间
func test(){print("函数开始了")defer {print("执行 defer1")}print("函数执行中")defer {print("执行 defer2")}print("函数将结束")}
test()/**函数开始了函数执行中函数将结束执行 defer2执行 defer1*/
处理错误异常
try 手动 do-catch 捕捉异常try? 如果该方法出现了异常,则该方法返回 nil,正常则返回对应的对象try! 直接强制执行方法。如果该方法出现了异常,那么程序会崩溃
在可能出现异常的函数后面添加 throws,在调用的时候使用 do-catch,保证调用者对相应的错误进行处理。
throws 的使用很简单,只需要在可能出现异常的函数或者方法后面添加 throws。经过这个关键字修饰的函数,在调用的时候,需要程序员加上 do-catch 来调用。只需要使用 throws 进行修饰,就保证了以后的调用者必然需要对相应的错误进行处理。当然也可以不处理,但无论如何,错误被 throws 携带出来了,以后的维护和优化不需要重新做错误处理的设计,直接加上错误处理的逻辑即可。
Swift 中提供了 Error 协议,我们在开发中,如果要自定义自己的错误类型,一般会使用一个 Enum 来遵循 Error 协议,目的是享用 Error 已经包含的一些特性。
enum MyError: Error {case ErrorOne}func willThrow(_ type: Int) throws -> String {if type == 1 {throw MyError.ErrorOne}return "That's OK"}do {let str = try willThrow(1)print(str)} catch let error as MyError {print("throws: ", error)} catch {}
rethrows 针对的不是函数或者方法本身,而是他携带的闭包类型的参数,当他的闭包类型的参数 throws 的时候,我们要使用 rethrows 将这个异常向上传递。
enum MyError : Error {case ErrorOne}func willThrow(_ type: Int) throws -> String {if type == 1 {throw MyError.ErrorOne}return "That's OK"}func willRethrow(_ throwCall: (Int) throws -> String) rethrows {do {let result = try throwCall(1)print(result)} catch let error as MyError {throw error // 这里再次 throw} catch {}}do {try willRethrow(willThrow)} catch let error as MyError {print("rethorws: ", error)} catch {}
备注:[函数中的一些关键字]
