錯誤/異常
- 開發過程常見的錯誤
- 語法錯誤(編譯報錯)
- 邏輯錯誤
- 執行時錯誤(可能會導致閃退,一般也叫做異常)
- ...
自定義錯誤
- Swift 中可以通過 Error 協議自定義執行時的錯誤資訊
- 比如 Alamofire 中就定義了很多錯誤
enum MyError: Error {
case zero
case outOfRange(Int, Int)
}
複製程式碼
func divide(_ num1: Int, _ num2: Int) -> Int {
return num1 / num2
}
func divide1(_ num1: Int, _ num2: Int) -> Int? {
guard num2 != 0 else {
return nil
}
return num1 / num2
}
func divide2(_ num1: Int, _ num2: Int) throws -> Int {
guard num2 != 0 else {
throw MyError.zero
}
return num1 / num2
}
print(divide(1, 2))
print(divide1(1, 0))
do {
print("1")
print(try divide2(1, 0))
print("2")
} catch MyError.zero {
print("zero")
} catch {
print("other")
}
複製程式碼
丟擲 Error 後,try 下一句直到作用域結束的程式碼都將停止執行
處理 Error
- 通過 do-catch 捕捉 Error
- 不捕捉 Error,在當前函式增加 throws 宣告,Error將自動拋給上層函式
錯誤捕捉
- catch is SomeError
- catch let error as SomeError
try?、try!
- 可以使用 try?、try! 呼叫可能會丟擲 Error 的函式,這樣就不用去處理 Error
func test(_ i: Int) throws -> Int {
if i == 0 {
throw MyError.zero
}
return 1
}
print("begin")
print(try? test(0))
print(try? test(1))
print(try! test(1))
print("end")
// 和 try? test(0) 是等價的
var b: Int?
do {
b = try test(0)
} catch {
b = nil
}
------------------------執行結果------------------------
begin
nil
Optional(1)
1
end
複製程式碼
rethrows
- rethrows 表明:函式本身不會丟擲錯誤,但呼叫閉包引數丟擲錯誤,那麼他會將錯誤向上拋
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows -> Int {
}
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
複製程式碼
defer
- 用來定義以任何方式離開程式碼塊前必須要執行的程式碼
- 將延遲至當前作用域結束之前執行
func open() {
print("open file")
}
func close() {
print("close file")
}
func test(_ i: Int) throws -> Int {
if i == 0 {
throw MyError.zero
}
return 1
}
open()
defer {
close()
}
try test(0)
複製程式碼
泛型
func swapValue<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var a = 10
var b = 20
swap(&a, &b)
var c = 10.0
var d = 20.0
swap(&c, &d)
-------------------------彙編分析------------------------
movq $0xa, -0x8(%rbp)
movq $0x14, -0x10(%rbp)
movq 0xd929(%rip), %rdx ; (void *)0x00007fff8afc1b58: type metadata for Swift.Int
leaq -0x8(%rbp), %rdi
leaq -0x10(%rbp), %rsi
callq 0x10001182c ; symbol stub for: Swift.swap<A>(inout A, inout A) -> ()
movq 0xd8f1(%rip), %rdx ; (void *)0x00007fff8afc14e0: type metadata for
callq 0x10001182c ; symbol stub for: Swift.swap<A>(inout A, inout A) -> ()
複製程式碼
可以看出來,這個裡面主要是傳進去了一個 Int.self 和 Double.self,根據這個去類的資訊裡面查詢相應的方法,然後執行
關聯型別(Associated Type)
- 關聯型別的作用:給協議中用到的型別定義一個佔位名稱
- 協議中可以擁有多個關聯型別
protocol Stackable {
associatedtype Element: Equatable
}
複製程式碼
型別約束
就是可以約束泛型
func swapValue<T: Equatable>(_ a: inout T, _ b: inout T)
複製程式碼
不透明型別
- 使用 some 的時候只允許返回一種型別,不允許返回兩種型別
- 除了用在返回值型別上,一般還可以用在屬性型別上
- 為了遮蔽的類內部的實現 原因:當 protocol 中有關聯型別時,如果返回的是 protocol 那麼他在編譯的時候沒有辦法,確定是哪個類
class Person: Runnable {
var speed: Int = 0
}
class Car: Runnable {
var speed: Double = 0.0
}
報錯:Protocol 'Runnable' can only be used as a generic constraint because it has Self or associated type requirements
func get(_ type: Int) -> Runnable {
if type == 0 {
return Person()
}
return Car()
}
解決方案一:
func get1<T: Runnable>(_ type: Int) -> T {
if type == 0 {
return Person() as! T
}
return Car() as! T
}
解決方案二:使用 some
func get2(_ type: Int) -> some Runnable {
return Car()
}
複製程式碼
用於遮蔽內部細節
class Dog: Runnable {
var speed: Int = 10
}
class Person {
var pet: some Runnable {
return Dog()
}
}
複製程式碼