十四、Error處理莽红、泛型

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

錯(cuò)誤類型

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

  • 語法錯(cuò)誤(編譯報(bào)錯(cuò))
  • 邏輯錯(cuò)誤 (偏離開發(fā)人員本意)
  • 運(yùn)行時(shí)錯(cuò)誤(可能會(huì)閃退妥畏,一般也叫做異常)
    ...
自定義錯(cuò)誤
  1. Swift可以通過Error協(xié)議自定義運(yùn)行時(shí)的錯(cuò)誤信息
  2. 函數(shù)內(nèi)部通過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. 通過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上拋 如果處理詳盡,可以無需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 {//此處無法處理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上拋 如果處理詳盡,可以無需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語句:用來定義以任何方式(拋錯(cuò)誤豁状,return等)離開代碼塊前必須要執(zhí)行的代碼
  • defer語句將在延遲至當(dāng)前作用域結(jié)束之前執(zhí)行
  • defer語句的執(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. 很多編程語言都有斷言機(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)重問題霞掺,希望結(jié)束程序運(yùn)行時(shí),可以直接使用fatalError函數(shù)拋出錯(cuò)誤(該錯(cuò)誤無法通過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)判斷類型 無需指明類型(即使加上也不會(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)//編譯通過
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í),無法明確對(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)

上面的問題业舍,解決方案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)

疑問:既然只返回一種類型,這個(gè)some不是很多余嗎升酣?
答:并不多余 可以隱藏返回的真實(shí)類型 且Car中的方法是無法調(diào)用的 只能訪問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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市噩茄,隨后出現(xiàn)的幾起案子下面,更是在濱河造成了極大的恐慌,老刑警劉巖绩聘,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沥割,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凿菩,警方通過查閱死者的電腦和手機(jī)机杜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衅谷,“玉大人椒拗,你說我怎么就攤上這事。” “怎么了蚀苛?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵在验,是天一觀的道長。 經(jīng)常有香客問我堵未,道長腋舌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任渗蟹,我火速辦了婚禮块饺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拙徽。我一直安慰自己刨沦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布膘怕。 她就那樣靜靜地躺著惧磺,像睡著了一般左敌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天葫哗,我揣著相機(jī)與錄音棚辽,去河邊找鬼苍息。 笑死疾渴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的髓堪。 我是一名探鬼主播送朱,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼干旁!你這毒婦竟也來了驶沼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤争群,失蹤者是張志新(化名)和其女友劉穎回怜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體换薄,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玉雾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轻要。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片复旬。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖冲泥,靈堂內(nèi)的尸體忽然破棺而出赢底,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布幸冻,位于F島的核電站,受9級(jí)特大地震影響咳焚,放射性物質(zhì)發(fā)生泄漏洽损。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一革半、第九天 我趴在偏房一處隱蔽的房頂上張望碑定。 院中可真熱鬧,春花似錦又官、人聲如沸延刘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碘赖。三九已至,卻和暖如春外构,著一層夾襖步出監(jiān)牢的瞬間普泡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工审编, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撼班,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓垒酬,卻偏偏與公主長得像砰嘁,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勘究,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

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