异常处理
你用过的软件是否经常崩溃或者做一些出乎你意料的事情?大部分情况下,这些问题是不正确的错误处理造成的。错误处理是软件开发中的幕后英雄:没人认为它重要;如果做好了,没人会注意到。同时它又非常关键:如果没做好,软件的用户肯定会注意到(而且会抱怨)。本章会探索Swift提供的捕获和处理错误的工具。
在Swift中也异常分为需查和不需查异常两类,但一般称为可恢复的错误和不可恢复的错误。一个异常的例子
var position: String.CharacterView.Index
expression failed to parse:
error: MyPlayground.playground:16:26: error: 'CharacterView' is unavailable: Please use String directly
var position: String.CharacterView.Index
语法格式
do{
try //指定可能会出现问题的代码,可以存在多个try
}
catch{ //可以存在多个catch
}
自定义异常
下面是一个综合的例子,包括自定义异常类型,以及如何抛出异常。
class Lexer {
enum Error: Swift.Error {
case invalidCharacter(Character)
}
}
手功抛出异常
需要用到关键字 throw
class Lexer {
enum Error: Swift.Error {
case invalidCharacter(Character)
}
func lex() throws -> [Token] { //Token为一自定义的枚举对象
var tokens = [Token]()
throw Lexer.Error.invalidCharacter(nextCharacter)
return tokens
}
}
稍复杂一点的例子
class Parser {
enum Error: Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
func getNumber() throws -> Int {
guard let token = getNextToken() else {
throw Parser.Error.unexpectedEndOfInput
}
switch token {
case .number(let value):
return value
case .plus:
throw Parser.Error.invalidToken(token)
}
}
func parse() throws -> Int {
while let token = getNextToken() {
switch token {
case .plus:
let nextNumber = try getNumber()
value += nextNumber
case .number:
throw Parser.Error.invalidToken(token)
}
}
return value
}
}
捕获异常
定义一个自定义的异常类型,需要用到关键字catch
class Parser {
enum Error: Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
}
一个综合的例子
//Lexer为自定义的类,实现暂时可忽略
func evaluate(_ input: String) {
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex() //try
let parser = Parser(tokens: tokens)
let result = try parser.parse() //try
} catch Lexer.Error.invalidCharacter(let character) {
print("Input contained an invalid character: \(character)")
}
catch Parser.Error.unexpectedEndOfInput {
print("Unexpected end of input during parsing")
}
catch Parser.Error.invalidToken(let token) {
print("Invalid token during parsing: \(token)")
}
catch { //通用异常捕获,而且这行必须要不,这是swift一个强制规定
print("An error occurred: \(error)")
}
}
测试代码
evaluate("10 + 3 + 5")
//输出如下
expression failed to parse:
error: exception.xcplaygroundpage:141:17: error: cannot find 'Lexer' in scope
let lexer = Lexer(input: input)
^~~~~
error: exception.xcplaygroundpage:147:22: error: cannot find 'Parser' in scope
let parser = Parser(tokens: tokens)
^~~~~~
error: exception.xcplaygroundpage:150:13: error: cannot find 'Lexer' in scope
} catch Lexer.Error.invalidCharacter(let character) {
^~~~~
error: exception.xcplaygroundpage:152:13: error: cannot find 'Parser' in scope
} catch Parser.Error.unexpectedEndOfInput {
^~~~~~
error: exception.xcplaygroundpage:154:13: error: cannot find 'Parser' in scope
} catch Parser.Error.invalidToken(let token) {
^~~~~~
不可查异常
- try!:如果遇到错误则强制抛出异常,它可以不放在do{}中,比如:
//实例化一个Lexer
let lexInstance = Lexer()
//如果 lexInstance.lex() 代码出现了错误,则无法捕获,整个程序就会退出。
let lexToken = try!lexInstance.lex()
异常忽略
- try?:如果遇到错误则可以忽略异常,它可以不放在do{}中,比如:
let lexInstance = Lexer()
guard let lexToken = try? lexInstance.lex() else{
return
}
上面这行代码会在异常时返回nil,注意处理nil的情况
if lexToken == nil {
}
程序预处理
typealias 设置别名
下面的代码表示把系统提供的Double重新命名为Velocity,这样系统中可以同时使用不同的关系字来表示Double的浮点数了。
typealias Velocity = Double
//使用
var dou:Velocity = 12;
typealias 的位置不固定,可以定义在类外面,也可以定义在类里面,比如:
class Accountant {
typealias NetWorthChanged = (Double) -> Void
var netWorthChangedHandler: NetWorthChanged? = nil
var netWorth: Double = 0.0 {
didSet {
netWorthChangedHandler?(netWorth)
}
}
func gained(_ asset: Asset, completion: () -> Void) {
netWorth += asset.value
completion()
}
}