錯誤類型
- 開發(fā)過程常見的錯誤
□ 語法錯誤(編譯報錯)
□ 邏輯錯誤
□ 運行時錯誤(可能會導(dǎo)致閃退,一般也叫做異常)
□ ......
自定義錯誤
- Swift中可以通過Error協(xié)議自定義運行時的錯誤信息
enum SomeError : Error {
case illegalArg(String)
case outOfBounds(Int, Int)
case outOfMemory
}
- 函數(shù)內(nèi)部通過throw拋出自定義Error,可能會拋出Error的函數(shù)必須加上throws聲明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0不能作為除數(shù)")
}
return num1 / num2
}
- 需要使用try調(diào)用可能會拋出Error的函數(shù)
var result = try divide(20, 10)
do-catch
- 可以使用do-catch捕捉Error
func test() {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
} catch let SomeError.illegalArg(msg) {
print("參數(shù)異常:", msg)
} catch let SomeError.outOfBounds(size, index) {
print("下標越界:", "size=\(size)", "index=\(index)")
} catch SomeError.outOfMemory {
print("內(nèi)存溢出")
} catch {
print("其他錯誤")
}
print("4")
}
test()
// 1
// 2
// 參數(shù)異常: 0不能作為除數(shù) // 4
- 拋出Error后,try下一句直到作用域結(jié)束的代碼都將停止運行
處理Error
- 處理Error的2種方式
① 通過do-catch捕捉Error
② 不捕捉Error,在當前函數(shù)增加throws聲明,Error將自動拋給上層函數(shù)
?? 如果最頂層函數(shù)(main函數(shù))依然沒有捕捉Error,那么程序?qū)⒔K止
func test() throws {
print("1")
print(try divide(20, 0))
print("2")
}
try test()
// 1
// Fatal error: Error raised at top level
do {
print(try divide(20, 0))
} catch is SomeError {
print("SomeError")
}
func test() throws {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
} catch let error as SomeError {
print(error)
}
print("4") }
try test()
// 1
// 2
// illegalArg("0不能作為除數(shù)") // 4
try?、try!
- 可以使用try?、try!調(diào)用可能會拋出Error的函數(shù),這樣就不用去處理Error
func test() {
print("1")
var result1 = try? divide(20, 10) // Optional(2), Int?
var result2 = try? divide(20, 0) // nil
var result3 = try! divide(20, 10) // 2, Int
print("2")
} test()
- a、b是等價的
var a = try? divide(20, 0)
var b: Int?
do {
b = try divide(20, 0)
} catch { b = nil }
rethrows
- rethrows表明:函數(shù)本身不會拋出錯誤,但調(diào)用閉包參數(shù)拋出錯誤,那么它會將錯誤向上拋
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1, num2))
}
// Fatal error: Error raised at top level
try exec(divide, 20, 0)
defer
defer語句:用來定義以任何方式(拋錯誤、return等)離開代碼塊前必須要執(zhí)行的代碼
defer語句將延遲至當前作用域結(jié)束之前執(zhí)行
func open(_ filename: String) -> Int {
print("open")
return 0
}
func close(_ file: Int) {
print("close")
}
- defer語句的執(zhí)行順序與定義順序相反
func fn1() { print("fn1") }
func fn2() { print("fn2") }
func test() {
defer { fn1() }
defer { fn2() }
}
test()
// fn2
// fn1
func processFile(_ filename: String) throws {
let file = open(filename)
defer {
close(file)
}
// 使用file
// ....
try divide(20, 0)
// close將會在這里調(diào)用
}
try processFile("test.txt")
// open
// close
// Fatal error: Error raised at top level
assert(斷言)
很多編程語言都有斷言機制:不符合指定條件就拋出運行時錯誤,常用于調(diào)試(Debug)階段的條件判斷
默認情況下,Swift的斷言只會在Debug模式下生效,Release模式下會忽略
func divide(_ v1: Int, _ v2: Int) -> Int {
assert(v2 != 0, "除數(shù)不能為0")
return v1 / v2
}
print(divide(20, 0))
- 增加Swift Flags修改斷言的默認行為
□ -assert-config Release:強制關(guān)閉斷言
□ -assert-config Debug:強制開啟斷言

image.png
fatalError
- 如果遇到嚴重問題,希望結(jié)束程序運行時,可以直接使用fatalError函數(shù)拋出錯誤(這是無法通過do-catch捕捉的錯誤)
□ 使用了fatalError函數(shù),就不需要再寫return
func test(_ num: Int) -> Int {
if num >= 0 {
return 1
}
fatalError("num不能小于0")
}
- 在某些不得不實現(xiàn)、但不希望別人調(diào)用的方法,可以考慮內(nèi)部使用fatalError函數(shù)
class Person { required init() {} }
class Student : Person {
required init() { fatalError("don't call Student.init") }
init(score: Int) {}
}
var stu1 = Student(score: 98) var stu2 = Student()
局部作用域
- 可以使用do 實現(xiàn)局部作用域
do {
let dog1 = Dog()
dog1.age = 10
dog1.run()
}
do {
let dog2 = Dog()
dog2.age = 10
dog2.run()
}