本文为学习笔记,对错误和异常的处理在我个人的代码中做的比较烂...而 Swift中有非常有些的处理错误机制,故将自己的学习记录成文.
反惯例,先上参考文章:
1.喵神的 Swifter必备 tips 第三版
2.Objc.cn
3.Swift 进阶(已更新到 Swift 3.0)
1.什么是异常和错误?
异常存在在我们每天的编码中,所有导致程序无法运行的问题都可以称之为异常.比如向一个无法响应的对象发送了消息或者访问超过数组元素下标都会产生异常,从而导致APP crash.
而错误是可能存在的合理的问题,比如登录错误或者一些 api 的 error,但是在 oc 中遇到传入 NSErrorPointer 指针去存储错误信息,但是我个人都是一般都是传个 nil....
2.Swift带来了什么新变化?
在 Swift2.0之后,这门语言引入了异常机制,带有 NSError的 API变成了可以抛出异常的API.
public func map(_ transform: (Element) throws -> T) rethrows -> [T]
含有 throws 的方法会抛出一个异常,而 rethrows和 throws 做的是一样的事情, rethrows 一般是用在参数中含有 throws 的方法中,如上, map函数可以接受一个 transform 函数,也可以一个能抛出异常的函数作为参数.
我来贴下我改造过的登录功能:
1.先定义一个用来表示错误类型的 Enum, 它遵从 Error protocol(Swift3)(ErrorType Swift2),它是一个空的 protocol 的,它只用来标示这个类型是个错误类型.
enum LoginError: Error {
case ErrorPassword(Int) //密码错误
case UserNotFound //账号错误
}
2.再开始写登录方法,这是一个可以抛出异常的方法,所以要把 throws 写在函数声明以后:
func login(user: String, password: String) throws {
if user != "admin" {
throw LoginError.UserNotFound // 抛出异常
}
if password != "password" {
throw LoginError.ErrorPassword(404) // 抛出异常
}
print("login success") // 登录成功
}
3.在调用会抛出异常的方法前面使用 try 关键字;如果要捕获抛出的异常,就需要吧会抛出异常额代码放在 do 包含的代码块里;在使用 catch 关键字来匹配各种需要捕获的异常.
do {
try login(user: "admina", password: "password")
} catch LoginError.UserNotFound {
print("UserNotFound")
} catch let LoginError.ErrorPassword(errorCode) {
print("ErrorPassword")
print(errorCode)
}
//log: UserNotFound
如果在确定这个函数调用不会发生异常,我们可以使用 try! 来调用:
try! login(user: "admina", password: "password")
这样来调用就不用把代码块放在 do catch 的代码块中,但是这样写一旦这个调用抛出异常,Swift 就会引发运行时异常.
3.错误类型结合泛型
还是使用登录模块,如果把得到的结果和错误异常都看作是一个单独的类型,然后把成功和错误看成了两种不同的情况,那么:
enum LoginResult {
case success(T)
case failure(String)
}
使用一个泛型的 enum 来表示返回的结果,当调用成功时,就返回. success 中的 T类型,如果失败,则返回. failure 中的 String,返回失败的原因.
func userLogin(user: String, password: String) -> LoginResult {
if user != "admin" {
return LoginResult.failure("error userName")
}
if password != "password" {
return LoginResult.failure("error password")
}
guard let value = "login success" as? T else {
return LoginResult.failure("some error")
}
return LoginResult.success(value)
}
那么登录的函数就可以返回一个带泛型的 enum, 可以是返回结果统一化.
let result: LoginResult = userLogin(user: "admin", password: "password")
switch result {
case .success(let result):
print(result)
case .failure(let errorMessage):
print(errorMessage)
}
4.总结
在 Swift 的编程中,因为 Swift 的独特性,最好不要忽视错误类型.灵活使用 throws 和结合泛型能让我们的代码即优雅又安全.
//:TO DO(xxx)