站在彙編角度深入瞭解 Swift(十二)

小星星_ios發表於2020-04-05

錯誤/異常

  • 開發過程常見的錯誤
    • 語法錯誤(編譯報錯)
    • 邏輯錯誤
    • 執行時錯誤(可能會導致閃退,一般也叫做異常)
    • ...

自定義錯誤

  • 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()
    }
}
複製程式碼

相關文章