十四、Error處理、泛型

錯(cuò)誤處理(異常處理)

錯(cuò)誤類型

開發(fā)過(guò)程中常見的錯(cuò)誤:

  • 語(yǔ)法錯(cuò)誤(編譯報(bào)錯(cuò))
  • 邏輯錯(cuò)誤 (偏離開發(fā)人員本意)
  • 運(yùn)行時(shí)錯(cuò)誤(可能會(huì)閃退,一般也叫做異常)
    ...
自定義錯(cuò)誤
  1. Swift可以通過(guò)Error協(xié)議自定義運(yùn)行時(shí)的錯(cuò)誤信息
  2. 函數(shù)內(nèi)部通過(guò)throw拋出自定義Error,可能會(huì)拋出Error的函數(shù)必須加上throws申明
  3. 需要使用try調(diào)用可能會(huì)拋出Error的函數(shù)
enum MyError :Error {
    case illegalArt(String)
}

func divide(_ num1: Int,_ num2: Int) throws -> Int{
    if num2 == 0 {
        throw MyError.illegalArt("0不能作為除數(shù)")
    }
    return num1 / num2
}

var result = try divide(20, 0)

print(result)
do-catch捕捉Error
  • 使用 do-catch 捕捉Error
  • 拋出Error后,try下一句直到作用域結(jié)束的代碼都將停止運(yùn)行
enum MyError :Error {
    case illegalArt(String)
    case outOfBounds(Int, Int)
    case outOfMemory
}

func divide(_ num1: Int,_ num2: Int) throws -> Int{
    if num2 == 0 {
        throw MyError.illegalArt("0不能作為除數(shù)")
    }
    return num1 / num2
}

func test() throws {
    print("1")
    do {
        print("2")
        print(try divide(20, 0))
        print("3")
    } catch let MyError.illegalArt(tip) {
        print("參數(shù)異常:",tip)
    }catch let MyError.outOfBounds(size, index){
        print("下標(biāo)越界異常:size = \(size),index = \(index)")
    }catch let MyError.outOfMemory{
        print("內(nèi)存溢出")
    }catch{
        print("其他錯(cuò)誤")
    }
}

try test()
/**
 輸出:
 1
 2
 參數(shù)異常: 0不能作為除數(shù)
 */
處理Error的2種方式
  1. 通過(guò)do-catch捕捉Error
  2. 不捕捉Error,在當(dāng)前函數(shù)增加throws聲明,Error將自動(dòng)上拋給上層函數(shù)
  • 如果最頂層函數(shù)(main函數(shù))依然沒有捕捉Error,那么程序?qū)⒔K止


    程序終止
enum MyError :Error {
    case illegalArt(String)
    case outOfBounds(Int, Int)
    case outOfMemory
}

enum NewError :Error{
    case unKnownError(String)
}

func divide(_ num1: Int,_ num2: Int) throws -> Int{
    if num2 == 0 {
        throw MyError.illegalArt("0不能作為除數(shù)")
    }
    
    if num2 == 1 {
        throw NewError.unKnownError("任何數(shù)除以1結(jié)果都是其本身")
    }
    
    return num1 / num2
}

func test() throws {//第3、4個(gè)do-catch錯(cuò)誤處理沒有窮盡,所以需要throws上拋 如果處理詳盡,可以無(wú)需throws
    
    do{
        print(try divide(20, 0))
    } catch let error as MyError {//強(qiáng)制類型轉(zhuǎn)換成功
        print(error)
    }catch{
        print("其他錯(cuò)誤")
    }
    
    do{
        print(try divide(20, 1))
    } catch let error as MyError {//強(qiáng)制類型轉(zhuǎn)換失敗
        print(error)
    }catch{
        print("其他錯(cuò)誤")
    }
    
    
    do{
        print(try divide(20, 0))
    } catch is MyError {//判斷錯(cuò)誤類型
        print("MyError")
    }
    
    do{
        print(try divide(20, 1))
    } catch is MyError {//此處無(wú)法處理Error上拋 程序終止
        print("MyError")
    }
}

try test()
/*輸出:
 illegalArt("0不能作為除數(shù)")
 其他錯(cuò)誤
 MyError
 Fatal error: Error raised at top level: example.NewError.unKnownError("任何數(shù)除以1結(jié)果都是其本身"):
 */

第3、4個(gè)do-catch錯(cuò)誤處理沒有窮盡,所以需要throws上拋 如果處理詳盡,可以無(wú)需throws

try?、 try!
  • 可以使用try?、try!調(diào)用可能會(huì)拋出Error的函數(shù),這樣就不用去處理Error
func test(){
    var result1 = try? divide(20, 10)//Optional(2),Int?
    var result2 = try? divide(20, 0)//nil
    var result3 = try! divide(20, 10)//2, Int
}

test()

//下面a與b完全等價(jià)
var a = try? divide(20, 0)

var b: Int?
do {
    b = try divide(20, 0)
}catch{
}
rethrows
  • rethrows表明:函數(shù)本身不會(huì)拋出錯(cuò)誤,但調(diào)用閉包參數(shù)拋出錯(cuò)誤,那么它將錯(cuò)誤向上拋
  • ??空合運(yùn)算符就是這么聲明的
    ?? 函數(shù)聲明
var fn = { (a: Int,b: Int) throws -> Int in
    a / b
}//閉包表達(dá)式

func exec(_ fn:(Int, Int) throws -> Int, _ num1:Int, _ num2: Int) rethrows  {
    print(try fn(num1,num2))
}

try exec(fn, 10, 20)
try exec(divide(_:_:), 10, 20)
defer
  • defer語(yǔ)句:用來(lái)定義以任何方式(拋錯(cuò)誤,return等)離開代碼塊前必須要執(zhí)行的代碼
  • defer語(yǔ)句將在延遲至當(dāng)前作用域結(jié)束之前執(zhí)行
  • defer語(yǔ)句的執(zhí)行順序,與定義順序相反
func open(_ fileName:String) -> Int{
    print("open")
    return 0
}

func close(_ file:Int) {
    print("close")
}

func processFile(_ fileName:String) throws {
    let file = open(fileName)
    defer {
        close(file)
    }
    //使用file
    //...
    do {
        try divide(20,0)
    } catch let error {
        switch error {
        case let MyError.illegalArt(tip):
            print(tip)
        default:
            print("其他錯(cuò)誤")
        }
    }
    
    //close將會(huì)在此處調(diào)用
}

try processFile("LOL.txt")
/**輸出:
 open
 0不能作為除數(shù)
 close
 */
func fn1() {
    print("fn1")
}

func fn2() {
    print("fn2")
}

func test() {
    defer {
        fn1()
    }
    
    defer {
        fn2()
    }
}
test()
/*輸出:
fn2
fn1
*/
斷言assert
  1. 很多編程語(yǔ)言都有斷言機(jī)制:不符合指定條件就拋出運(yùn)行時(shí)錯(cuò)誤,常用于調(diào)試(Debug)階段的條件判斷
  2. 默認(rèn)條件下,Swift的斷言只會(huì)在Debug模式下生效,Release模式下忽略
  3. 增加Swift Flags修改斷言的默認(rèn)行為
  • -assert-config Release 強(qiáng)制關(guān)閉斷言
  • -assert-config Debug 強(qiáng)制開啟斷言
fatalError
  • 如果遇到嚴(yán)重問(wèn)題,希望結(jié)束程序運(yùn)行時(shí),可以直接使用fatalError函數(shù)拋出錯(cuò)誤(該錯(cuò)誤無(wú)法通過(guò)do-catch捕捉)
  • 使用fatalError函數(shù),就不需要再寫return
  • 在某些不得不實(shí)現(xiàn),但不希望別人調(diào)用的方法,可以考慮內(nèi)部使用fatalError函數(shù)
局部作用域
  • 可以使用do 實(shí)現(xiàn)局部作用域

泛型(Generics)

泛型

泛型可以將類型參數(shù)化,提高代碼復(fù)用率,減少代碼量

//將類型參數(shù)化
func swapValues<T>(_ a: inout T , _ b: inout T){
    (a,b) = (b,a)
}

var v1 = 10
var v2 = 20
swap(&v1, &v2)//v1:20 v2: 10

var a1 = 10.5
var a2 = 20.6
swap(&a1, &a2)//v1:20.6 v2: 10.5

struct Date {
    var year = 0
    var month = 0
    var day = 0
}
var date1 = Date(year: 2019, month: 12, day: 31)
var date2 = Date(year: 2020, month: 01, day: 01)

swap(&date1, &date2)
//date1:Date(year: 2020, month: 1, day: 1)
//date2:Date(year: 2019, month: 12, day: 31)

//泛型函數(shù)賦值給變量

func test<T1,T2>(_ a: T1, _ b: T2) {
    
}
var fn :(Int, Double)->() = test(_:_:)//此時(shí)確定參數(shù)類型
//類
class Stack <Element>{
    var elements = [Element]()
    func push(_ element:Element) {
        elements.append(element)
    }
    func pop() -> Element {
        elements.removeLast()
    }
    func top() -> Element {
        elements.last!
    }
    func size() -> Int {
        elements.count
    }
}

//類繼承
class SubStack<Element> : Stack<Element> {
    
}

var stack = Stack<Int>()
stack.push(11)
stack.push(22)
stack.push(33)

print(stack.top())//33
print(stack.pop())//33
print(stack.pop())//22
print(stack.size())//1
//結(jié)構(gòu)體
struct Stack <Element>{
    var elements = [Element]()
    //結(jié)構(gòu)體、枚舉中修改自身內(nèi)存必須要加mutating
    mutating func push(_ element:Element) {
        elements.append(element)
    }
    mutating func pop() -> Element {
        elements.removeLast()
    }
    func top() -> Element {
        elements.last!
    }
    func size() -> Int {
        elements.count
    }
}

//在類、結(jié)構(gòu)體、枚舉的初始化器中,如已添加元素,則根據(jù)元素類型自動(dòng)判斷類型 無(wú)需指明類型(即使加上也不會(huì)錯(cuò))
var stack = Stack<Double>(elements: [10])//10可以賦值給Double類型
stack.push(11)
stack.push(22)
stack.push(33)

print(stack.top())//33
print(stack.pop())//33
print(stack.pop())//22
print(stack.size())//2

//枚舉
enum Score <T> {
    case point(T)
    case grade(String)
}

let score1 = Score.point(100)
let score2 = Score<Int>.point(95)
let score3 = Score.point(99.5)
let score4 = Score<Int>.grade("A")//此處聲明的類型為point的泛型類型 需要確定枚舉類型分配內(nèi)存

print(score1,score2,score3,score4)
關(guān)聯(lián)類型(Associated Type)
  • 作用:給協(xié)議中用到的類型定義一個(gè)占位名稱
  • 協(xié)議中可以擁有多個(gè)關(guān)聯(lián)類型
protocol Stackable {
    associatedtype Element//關(guān)聯(lián)類型
    
    mutating func push(_ element:Element)
    mutating func pop() -> Element
    func top() -> Element
    func size() -> Int
}

//給關(guān)聯(lián)類型設(shè)定真實(shí)類型
class Stack : Stackable{
    typealias Element = Int
    //或者給所有的用到的Element換為String
    
    var elements = [Element]()
    //結(jié)構(gòu)體、枚舉中修改自身內(nèi)存必須要加mutating
    func push(_ element:Element) {
        elements.append(element)
    }
    func pop() -> Element {
        elements.removeLast()
    }
    func top() -> Element {
        elements.last!
    }
    func size() -> Int {
        elements.count
    }
}

//使用泛型
class Stack <E>: Stackable{
    var elements = [E]()
    //結(jié)構(gòu)體、枚舉中修改自身內(nèi)存必須要加mutating
    func push(_ element:E) {
        elements.append(element)
    }
    func pop() -> E {
        elements.removeLast()
    }
    func top() -> E {
        elements.last!
    }
    func size() -> Int {
        elements.count
    }
}
類型約束
protocol Stackable {
    associatedtype Element :Equatable
}

class Stack<E: Equatable>: Stackable {
    typealias Element = E
}

func equal<S1: Stackable,S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element,S1.Element : Hashable{
    return false
}//S1 S2必須遵守Stackable 并且S1和S2的關(guān)聯(lián)類型相同 且S1的管理按類型是遵守Hashable協(xié)議

var s1 = Stack<Int>()
var s2 = Stack<Int>()
var s3 = Stack<String>()

equal(s1, s2)//編譯通過(guò)
equal(s1, s3)//編譯報(bào)錯(cuò)  function 'equal' requires the types 'Int' and 'String' be equivalent
協(xié)議類型的注意點(diǎn)
  • 如果協(xié)議中有associatedtype 會(huì)下面的報(bào)錯(cuò),具有“自身”或關(guān)聯(lián)的類型要求的協(xié)議只能用作通用約束,在初始化時(shí),無(wú)法明確對(duì)象類型
    那這種情況如何解決呢
泛型解決方案
protocol Runnable {
    associatedtype Speed
    var speed:Speed { get }
}

class Person : Runnable{
    var speed: Double {
        4.5
    }
}

class Car : Runnable{
    var speed: Int {
        100
    }
}

func get<T:Runnable>(_ type: Int) -> T {
    if type == 0 {
        return Person() as! T//強(qiáng)制類型轉(zhuǎn)換有風(fēng)險(xiǎn) 慎用!此處僅解決上述報(bào)錯(cuò)
    }
    return Car() as! T
}

//如果類型聲明與泛型不同則報(bào)錯(cuò)
var r1:Person = get(0)//Person實(shí)例
var r2:Car = get(1)//Car實(shí)例
不透明類型(Opaque Tpye)

上面的問(wèn)題,解決方案2:使用Opaque Tpye

//some 只能返回一種類型
func get(_ type: Int) -> some Runnable {
//    if type == 0 { //取消注釋即報(bào)錯(cuò)
//        return Person()
//    }
    return Car()
}

var r1 = get(0)
var r2 = get(1)

疑問(wèn):既然只返回一種類型,這個(gè)some不是很多余嗎?
答:并不多余 可以隱藏返回的真實(shí)類型 且Car中的方法是無(wú)法調(diào)用的 只能訪問(wèn)procotol中的speed屬性

some

some 除了可以用在返回值類型上,一般還可以用在屬性類型上

protocol Runnable {
    associatedtype Speed
    var speed:Speed { get }
}


class Dog : Runnable{
    typealias Speed = Double
    
    var speed: Speed{
        3.0
    }
}

class Person{
    var pet:some Runnable {
        return Dog()
    }
}
do實(shí)現(xiàn)局部作用域

可以單獨(dú)使用do實(shí)現(xiàn)局部作用域


do{
    let value1:Int = 10
    print(value1)
}

do{
    let value1:Int = 10
    print(value1)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一. 錯(cuò)誤處理 開發(fā)中常見的錯(cuò)誤: 語(yǔ)法錯(cuò)誤(編譯時(shí)會(huì)報(bào)錯(cuò)) 邏輯錯(cuò)誤 運(yùn)行時(shí)錯(cuò)誤(可能會(huì)導(dǎo)致閃退,一般也叫做異常...
    Imkata閱讀 877評(píng)論 0 0
  • 1.Swift中的錯(cuò)誤處理方式 Swift中錯(cuò)誤用符合Error協(xié)議的類型表示。一般是枚舉enum或者結(jié)構(gòu)體str...
    coder_zhengyi閱讀 1,274評(píng)論 0 1
  • Swift 中的錯(cuò)誤處理 將可能遇到的異常盡可能扼殺在編譯器是 Swift 在安全性上至始至終貫徹的理念,例如之前...
    CodingIran閱讀 1,965評(píng)論 0 1
  • 1、范型范型所解決的問(wèn)題 函數(shù)、方法、類型:類,結(jié)構(gòu)體,枚舉,元組類型,協(xié)議參數(shù),返回值,成員函數(shù)參數(shù),成員屬性類...
    我是小胡胡123閱讀 941評(píng)論 0 1
  • 每天起床我都會(huì)仰望它,小女兒說(shuō)這是她的小寶寶,還輕輕的親吻它。
    愛畫畫的紫竹梅閱讀 77評(píng)論 0 0

友情鏈接更多精彩內(nèi)容