跟其它语言一样,Swift的异常处理是在程序抛出异常后的处理逻辑。 Swift提供了一流的异常抛出、捕获和处理的能力。跟Java语言类似, Swift的异常并不是真正的程序崩溃, 而是程序运行的一个逻辑分支;Swift和Java捕获异常的时序也是一样的。当Swift运行时抛出异常后并没有被处理, 那么程序就会崩溃。
在Swift语言中使用Error表示异常, 作用同Java的Exception类或Object-C的NSError类。 苹果建议使用枚举作为异常类型(为什么不推荐用类或者结构体?答案是枚举数据类型本身就是分成若干种情况,很适合做逻辑分支判断条件)。
enum VendingMathineError: Error {
case invalidSelection
case insufficientFunds(coinsNeed: Int)
case outOfStack
}
上面声明了枚举类型VendingMathineError,继承于Error。
注意Swift的所有异常类型都继承于Error, 就像Java所有异常类都继承于Exception一样。
类似于Java处理异常的try/catch/finally, Swift提供了try、try?、try!、catch、throw、throws关键字处理异常逻辑,用法跟Java也很像。
如何声明一个可能抛出异常的函数? 在函数参数括号后面添加throws关键字, 跟Java语法有点像;区别是Swift的throws后面不用跟着异常类、而Java的throws后面要有异常类名称。 你只要告诉Swift这个函数可能抛出异常就够了,不需要说明到底是哪种异常,函数体内可以抛出任意类型的异常(肯定是继承于Error)。
func canThrowErrors() throws -> String
func canThrowErrors(type: Int) throws -> String? {
if type == 1 {
throw VendingMathineError.invalidSelection
}
if type == 2 {
throw VendingMathineError.outOfStack
}
if type == 3 {
throw VendingMathineError.insufficientFunds(coinsNeed: 100)
}
return "success"
}
上面测试代码是为了测试抛异常逻辑, 函数体写成switch/case更好一些:)
从canThrowErrors函数看出,当参数为1、2或3时会抛异常, 语法是throw ... 且程序会跳出函数体,语法同Java。
函数体内必须有try且该语句可能抛出异常)、catch。
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
- }
注意:如果try语句抛出异常则会跳出do代码块,并按顺序逐个catch,当一个catch捕获成功后,后面的catch不再执行。
do {
var data = try canThrowErrors(type: 3)
print("after execute canThrowErrors")
if data != nil {
print("Error test data:\(data)")
}
} catch VendingMathineError.outOfStack {
print("outOfStack")
} catch VendingMathineError.invalidSelection {
print("invalidSelection")
} catch { //类似于Java的catch(Exception ex)
print("Error")
}
输出:Error
try canThrowsErrors(type: 3)会抛出VendingMathineError.isSufficientFunds(coinsNeed:100),不属于前2个catch类型, 而最后一个catch是捕获所有异常, 所有会执行其函数体。
是不是感觉少了点什么? 对, 少了个类似于Java的finally,后面会介绍。
下面再介绍一下try?和try!的用法。
let x = try? someThrowingFunction()
同
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
try?后面的语句可能会抛出异常, 如果抛出异常则赋值nil给左侧;如果没抛出异常则将返回值赋给左侧;
裸奔, 明知可能抛出异常,但自信这段代码不会抛异常。 try!是try?的补充。你确定后面语句不会抛出异常,但真的抛出异常后程序会崩溃。不建议使用try!,有使用场景推荐使用try?
let tmpX = try? canThrowErrors(type:1) //如果抛出异常程序正常运行并赋值nil给左侧,如果没抛异常则将返回值赋给左侧
//let tmpY = try! canThrowErrors(type: 2) //你很确定不会抛出异常时这样用,但如果运行时抛异常会导致程序崩溃
Swift的枚举很强大, 它可以带参数!!! 在抛出异常时可以携带参数, 这是其它语言不具备的特性。示例代码:
enum MyError: Error {
case Name(param: String)
case Age(param: Int)
}
func testMethod() throws {
throw MyError.Name(param: "zhangsan")
}
do {
try testMethod()
} catch MyError.Name(let name){
print(name)
} catch {
print(error) //默认处理, error是编译器添加的默认参数
}
Swift使用defer关键字作用同Java的finally, 即使代码块内有break、continue、return或者抛异常,在退出代码块后仍然会执行defer代码块。
func testDefer(_ param: Int) throws -> String {
print("testDefer begin")
defer {
print("testDefer exit")
}
// do something...
if param == 1 {
throw VendingMathineError.invalidSelection
}
print("testDefer end")
return "testDefer return"
}
let tmpZ = try? testDefer(1)
输出:
testDefer begin
testDefer exit
上面代码只是为了测试,验证函数体内抛出异常时的执行时序, 语法逻辑跟finally一模一样。
func testDeferNormal() {
print("testDefer begin")
defer {
print("testDefer exit")
}
print("testDefer end")
}
testDeferNormal()
输出:
testDefer begin
testDefer end
testDefer exit
Swift使用?? 提供类似于Java的 a==null? b:a的功能。
let favoriteSnacks = [
"Alice": "Chips",
"Bob" : "Licorice",
"Eve" : "Pretzels",
]
let snameName = favoriteSnacks["Alice"] ?? "other" //snameName等于"Chips"
let otherName = favoriteSnacks["some"] ?? "other" //otherName等于“other”
小结:
1、 Swift的Error类似于Java的Exception;
2、Swift的异常都是继承于Error类;
3、Swift使用do/catch类似于Java的try/catch;
4、Swift使用defer类似于Java的finally;
5、??等同于Java的三目运算符a==null? b:a;
6、注意try、try!和try?的用法和区别。
7、catch后不带参数可以捕获各种异常。
8、枚举Error可以携带参数!!! 比其它语言强大之处。