從零學(xué)習(xí)Swift 11: 錯(cuò)誤處理,泛型使用

總結(jié)

在 Swift 中可以通過(guò)Error協(xié)議自定義運(yùn)行時(shí)的錯(cuò)誤信息.任何實(shí)現(xiàn)了Error協(xié)議的枚舉,結(jié)構(gòu)體,類(lèi)都可以自定義錯(cuò)誤信息.

自定義錯(cuò)誤三部曲:
1: 實(shí)現(xiàn)Error協(xié)議


//第一步: 實(shí)現(xiàn) Error 協(xié)議:
enum MyError: Error{
    case urlError(String)
    case otherError(String)
}

2: 方法聲明和方法體:


//如果有可能拋出錯(cuò)誤的方法,需要在方法聲明中添加`throws`關(guān)鍵字
func isValidURL(url: String) throws{
    print("接口檢查")

    
    guard url.hasPrefix("http") else {
        //使用 throw 拋出錯(cuò)誤
        throw MyError.otherError("url有誤")
    }
    
    print("進(jìn)行網(wǎng)絡(luò)請(qǐng)求")
}

3: 調(diào)用方法時(shí)必須添加try關(guān)鍵字:


try isValidURL(url: "")

以上三步我們就自定義了一個(gè)錯(cuò)誤,并且把這個(gè)錯(cuò)誤拋了出去.但是這個(gè)錯(cuò)誤我們還沒(méi)有處理.如果錯(cuò)誤一直沒(méi)有被處理會(huì)造成程序崩潰:

錯(cuò)誤沒(méi)有處理造成崩潰

處理錯(cuò)誤的幾種方式:

  1. 使用do-catch捕捉錯(cuò)誤
do-catch 捕捉錯(cuò)誤

需要注意的是,一旦拋出錯(cuò)誤, try 下一句直到作用域結(jié)束的代碼都不會(huì)執(zhí)行,所以我們寫(xiě)代碼的時(shí)候要考慮好.

  1. 不捕捉錯(cuò)誤,使用throws將錯(cuò)誤一層層向上傳遞:
throws 傳遞錯(cuò)誤

在調(diào)用有可能會(huì)拋出錯(cuò)誤的當(dāng)前函數(shù)的聲明中加上throws關(guān)鍵字,錯(cuò)誤將自動(dòng)拋給上層調(diào)用的函數(shù),如果到最頂層的函數(shù)依然沒(méi)有捕捉錯(cuò)誤,程序仍然會(huì)崩潰.

  1. 使用try?,將函數(shù)調(diào)用結(jié)果封裝成可選項(xiàng),如果函數(shù)調(diào)用失敗,結(jié)果為nil,不需要我們處理錯(cuò)誤.

try? isValidURL(url: "")

rethrows
如果函數(shù)本身不會(huì)拋出錯(cuò)誤,但是函數(shù)的閉包參數(shù)可能會(huì)拋出錯(cuò)誤,這時(shí)就要使用rethrows聲明函數(shù):

rethrows

Swift 中的空合并運(yùn)算符其實(shí)就是方法,它的定義就使用了rethrows聲明:


public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T

public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?

defer延遲執(zhí)行
如果我們想控制一段代碼,不管以任何方式離開(kāi)其作用域前都會(huì)執(zhí)行,可以使用defer關(guān)鍵字:

defer
斷言 assert

我們還可以使用斷言assert來(lái)處理不符合指定條件時(shí)使程序發(fā)生運(yùn)行時(shí)錯(cuò)誤,強(qiáng)制退出:

//格式
assert(條件 , 提示語(yǔ))

示例

斷言只會(huì)在Debug模式下才會(huì)生效,在Release模式下會(huì)失效.
如果我們想讓斷言在Release模式下也生效,或者如果我們想讓斷言在Debug模式下失效,可以這樣設(shè)置:

設(shè)置斷言
fatalError

如果遇到嚴(yán)重問(wèn)題,想讓程序結(jié)束運(yùn)行,可以使用fatalError函數(shù)拋出錯(cuò)誤.
fatalErrorassert的區(qū)別是:assert默認(rèn)只在Debug模式向下才有效;fatalErrorDebugRelease模式下都有效.


func nickName(name: String){
    if name.count > 6 {
        //...
    }
    fatalError("nickName長(zhǎng)度不能小于6位")
}

nickName(name: "ok123")


函數(shù)中使用泛型

泛型在 Swift 中非常普遍,比如 Array數(shù)組:

Array 中的泛型

我們之前定義方法的時(shí)候都要寫(xiě)清楚參數(shù)類(lèi)型,比如Int , String , Double等等.其實(shí)我們也可以在聲明方法時(shí),參數(shù)類(lèi)型用泛型表示,不限定參數(shù)類(lèi)型,由具體傳入的實(shí)參推斷參數(shù)類(lèi)型:

比如:

泛型函數(shù)

從上圖可以看到,使用泛型作為參數(shù),不管傳入的實(shí)參是什么類(lèi)型,都能正確匹配上.那么Swift是怎樣實(shí)現(xiàn)泛型的呢?是不是它的底層采用函數(shù)重載的方式,根據(jù)傳入的實(shí)參自動(dòng)生成了很多同名的函數(shù)呢?我們可以通過(guò)匯編分析一下:

我們傳入不同類(lèi)型的的參數(shù)看看底層匯編:

傳入不同的參數(shù)

匯編分析:

匯編語(yǔ)言

從匯編中可以看到兩次調(diào)用test()函數(shù)的方法地址是相同的,也就是說(shuō)底層并沒(méi)有自動(dòng)生成其他函數(shù).其實(shí)泛型實(shí)現(xiàn)的關(guān)鍵就在于綠色標(biāo)注出來(lái)的部分.我們可以看到在調(diào)用test()方法之前會(huì)把實(shí)參的元類(lèi)型metadata也當(dāng)做參數(shù)傳入.而在metadata中有每個(gè)類(lèi)型詳細(xì)的信息,所以泛型能識(shí)別出各個(gè)類(lèi)型.

類(lèi),結(jié)構(gòu)體,枚舉中使用泛型

Swift 中的Array , Dictionary的定義中都使用了泛型:


//Array
public struct Array<Element> {...}

//Dictionary
public struct Dictionary<Key, Value> where Key : Hashable {...}

我們也可以在自己寫(xiě)的類(lèi)中使用泛型:


class Pet{
    
}

class Person<P>{
    var age: Int = 1
    //泛型
    var pets = [P]()
}

var person = Person<Pet>()

之前我們創(chuàng)建對(duì)象的時(shí)候直接類(lèi)型名稱 + ()就可以了,類(lèi)似這樣:


var person = Person()

但是如果類(lèi)中使用了泛型,就必須在創(chuàng)建實(shí)例時(shí)指明泛型的類(lèi)型,不然會(huì)報(bào)錯(cuò):

泛型類(lèi)型的創(chuàng)建

從給出的錯(cuò)誤中可以看到,編譯器無(wú)法推斷出泛型的類(lèi)型,所以報(bào)錯(cuò).

正確創(chuàng)建泛型實(shí)例應(yīng)該這樣:


var person = Person<Pet>()

Structclass的泛型使用是一樣的,Enum稍微有一個(gè)不同點(diǎn),需要注意一下:

枚舉中使用泛型

可以看到按照之前創(chuàng)建枚舉的方式會(huì)報(bào)錯(cuò),報(bào)的錯(cuò)誤是T無(wú)法被推斷出來(lái),和我們創(chuàng)建實(shí)例對(duì)象時(shí)候的錯(cuò)誤一樣.所以我們要在創(chuàng)建時(shí)指明泛型是哪種類(lèi)型,不然編譯器推斷不出來(lái),分配內(nèi)存時(shí)無(wú)法分配:


enum Myerror<T>{
    case number(Int)
    case text(T)
}

var error = Myerror<String>.number(1)

協(xié)議中使用泛型

協(xié)議中使用泛型比較特殊,必須使用associatedtype關(guān)鍵字:


//可以養(yǎng)寵物的協(xié)議
protocol Petable {
    associatedtype Pet
    func walkThePet(pet: Pet)
}

當(dāng)其他類(lèi)實(shí)現(xiàn)這個(gè)協(xié)議時(shí),需要指定協(xié)議中的關(guān)聯(lián)類(lèi)型的真實(shí)類(lèi)型:


class Person: Petable{
    
    typealias Pet = Dog
    
    func walkThePet(pet: Dog) {
        print("遛的寵物是:",pet.type)
    }
}

typealias Pet = Dog這句代碼可以省略掉,因?yàn)榫幾g器可以通過(guò)walkThePet(pet: Dog)的參數(shù)中推斷出關(guān)聯(lián)類(lèi)型是Dog:


class Person: Petable{
    
//    typealias Pet = Dog
    
    func walkThePet(pet: Dog) {
        print("遛的寵物是:",pet.type)
    }
}


泛型約束

我們可以通過(guò)泛型約束函數(shù)的參數(shù)類(lèi)型和返回值類(lèi)型:


//寵物證協(xié)議
protocol PetCard {}
//寵物
class Pet{}

//收養(yǎng)寵物:必須是Pet的子類(lèi),并且實(shí)現(xiàn)了PetCard協(xié)議
func adoptionPet(T: Pet & PetCard){
    
}

class Dog: Pet & PetCard{
}

class Pig: Pet{}

adoptionPet(T: Dog())
adoptionPet(T: Pig())

以上代碼就對(duì)adoptionPet ()方法的參數(shù)作了約束,只允許傳入的參數(shù)是Pet 的子類(lèi),并且實(shí)現(xiàn)了 PetCard 協(xié)議.如果不滿足任何一個(gè)條件就會(huì)報(bào)錯(cuò).

協(xié)議約束

where關(guān)鍵字:增加約束條件
如果我們的約束條件比較多,可以使用where關(guān)鍵字:


//寵物證協(xié)議
protocol PetCard {
    //關(guān)聯(lián)屬性
    associatedtype Pet
    func showCard()
}

//聽(tīng)話協(xié)議
protocol Tractable {
    
}

class Dog: PetCard , Tractable{
    typealias Pet = Dog
    func showCard() {
    
    }
    
    let type = "狗"
}

class Cat: PetCard{
    typealias Pet = Cat
    func showCard() {
        
    }
    
}

func play<T1: PetCard,T2: PetCard>(animal1: T1 , animal2: T2) where
    T1.Pet == T2.Pet , T1.Pet : Tractable
{
    
}

var dog = Dog()
var cat = Cat()
play(animal1: dog, animal2: cat)

上面代碼我們使用where關(guān)鍵字增加了兩個(gè)條件:

  1. 泛型T1 , T2中的Pet必須類(lèi)型相同
  2. T1還要實(shí)現(xiàn)Tractable協(xié)議

如果傳入的參數(shù)不符合條件,就會(huì)報(bào)錯(cuò):

不符合條件

含有關(guān)聯(lián)值類(lèi)型的協(xié)議作為函數(shù)返回值

我們經(jīng)常會(huì)使用協(xié)議作為函數(shù)的返回值,能夠限定返回值的類(lèi)型,比如這樣:

協(xié)議類(lèi)型作為函數(shù)返回值

但是如果協(xié)議中含有關(guān)聯(lián)值類(lèi)型,并且作為函數(shù)返回值,以上代碼就會(huì)報(bào)錯(cuò):

含有關(guān)聯(lián)類(lèi)型的協(xié)議作為返回值

為什么會(huì)報(bào)這種錯(cuò)呢?因?yàn)?/code>Petable協(xié)議中有個(gè)關(guān)聯(lián)了類(lèi)型Color.而getApet方法的返回值是不確定的.所以編譯器是不知道Color是哪種類(lèi)型,所以就出現(xiàn)了這種錯(cuò)誤.

解決這種錯(cuò)誤有兩種方式:

  1. 使用泛型解決
使用泛型解決
  1. some

使用some聲明一個(gè)不透明類(lèi)型

使用 some 后又出現(xiàn)了另一個(gè)錯(cuò)誤

但是使用some之后會(huì)出現(xiàn)另一個(gè)錯(cuò)誤.這是因?yàn)?/code>some有一個(gè)限制,只能返回一種類(lèi)型,不能一會(huì)兒是 cat , 一會(huì)兒是 dog

some要求只能返回一種類(lèi)型

所以如果想讓一個(gè)帶有關(guān)聯(lián)類(lèi)型的協(xié)議作為函數(shù)返回值,可以使用some關(guān)鍵字.但是必須只能返回一種類(lèi)型.

有人可能會(huì)覺(jué)得some關(guān)鍵字很多余,既然只能返回一種類(lèi)型,為什么直接把返回值寫(xiě)成Cat或者Dog呢?

因?yàn)橹苯訉?xiě)成Cat或者Dog,我們通過(guò)函數(shù)獲得的對(duì)象,外界就能直接看到是什么類(lèi)型,并且類(lèi)型中的屬性,方法也會(huì)暴露出去:

暴露類(lèi)型

而使用協(xié)議作為返回值可以對(duì)外隱藏真實(shí)類(lèi)型:

隱藏實(shí)際類(lèi)型

所以,如果有這種特殊需求:只想返回一個(gè)遵守某種協(xié)議的對(duì)象,并且只想把協(xié)議中的方法暴露出去,不想暴露真實(shí)類(lèi)型和方法,這時(shí)就可以使用 some 關(guān)鍵字.

some關(guān)鍵字還可以用在屬性中:

some 關(guān)鍵字用在屬性中

其實(shí)如果協(xié)議中沒(méi)有關(guān)聯(lián)類(lèi)型,也不會(huì)暴露真實(shí)類(lèi)型,只不過(guò)some關(guān)鍵字就是專門(mén)用來(lái)解決協(xié)議中有關(guān)聯(lián)類(lèi)型或者使用Self關(guān)鍵字的問(wèn)題:

協(xié)議中沒(méi)有關(guān)聯(lián)類(lèi)型也不會(huì)暴露真實(shí)類(lèi)型
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硝清,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子退敦,更是在濱河造成了極大的恐慌卵牍,老刑警劉巖妄痪,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镶蹋,警方通過(guò)查閱死者的電腦和手機(jī)凹联,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)沐兰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蔽挠,你說(shuō)我怎么就攤上這事住闯。” “怎么了澳淑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵比原,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我杠巡,道長(zhǎng)量窘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任氢拥,我火速辦了婚禮蚌铜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兄一。我一直安慰自己厘线,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布出革。 她就那樣靜靜地躺著造壮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耳璧,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天成箫,我揣著相機(jī)與錄音,去河邊找鬼旨枯。 笑死蹬昌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的攀隔。 我是一名探鬼主播皂贩,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昆汹!你這毒婦竟也來(lái)了明刷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤满粗,失蹤者是張志新(化名)和其女友劉穎辈末,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體映皆,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挤聘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捅彻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片组去。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沟饥,靈堂內(nèi)的尸體忽然破棺而出添怔,到底是詐尸還是另有隱情,我是刑警寧澤贤旷,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布广料,位于F島的核電站,受9級(jí)特大地震影響幼驶,放射性物質(zhì)發(fā)生泄漏艾杏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一盅藻、第九天 我趴在偏房一處隱蔽的房頂上張望购桑。 院中可真熱鬧,春花似錦氏淑、人聲如沸勃蜘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缭贡。三九已至炉擅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阳惹,已是汗流浹背谍失。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莹汤,地道東北人快鱼。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像纲岭,于是被迫代替她去往敵國(guó)和親抹竹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354