函数与方法
函数与方法本质上没啥区别。如果非要说区别,那么函数是独立存在的,而方法存在于结构体、类中。
定义函数
函数表达式:
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 in
return a + b
})
test(1, 2, fn: { (a, b) -> Int in
return a * b
})
// 3
// 2
函数作为返回值
func makePlus(forPlus amount: Int) -> () -> Int {
var total = 0
func plus() -> Int {
total += amount
return total
}
return plus
}
let block = makePlus(forPlus: 10)
print(block()) // 10
print(block()) // 20
print(block()) // 30
其他用法
修改外部变量的值
参数默认是常量,所以不能在函数内修改参数的值,更不能修改传递过来的外部变量的值。(值类型)
可以在参数的类型前面加 inout
关键字,表示内部会修改改变外部的变量,调用时要加 &
符号。
func swap( a: inout Int, b: inout Int) {
let tt = a
a = b
b = tt
}
var a = 1, b = 2
swap(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 {
}
备注:[函数中的一些关键字]