Swift 5.1 (17) - 類(lèi)型轉(zhuǎn)換與模式匹配

類(lèi)型轉(zhuǎn)換在Swift中使用isas操作符實(shí)現(xiàn)窄瘟。

類(lèi)型檢查

使用操作符is檢查一個(gè)實(shí)例是否是某個(gè)確定的類(lèi)以及其繼承體系的父類(lèi)或子類(lèi)類(lèi)型疏尿。如果是某個(gè)確定的類(lèi)(該類(lèi)繼承體系的父類(lèi)或子類(lèi))類(lèi)型芝硬,則返回true泻蚊,否則返回false栅哀。

class Cat {
    func hairColor() -> String {
        return "五顏六色"
    }
}
class WhiteCat: Cat {
    override func hairColor() -> String {
        return "白色"
    }
}
class BlackCat: Cat {
    override func hairColor() -> String {
        return "黑色"
    }
}
//必須符合`Cat`類(lèi)以及其子類(lèi)溃睹,類(lèi)型推斷需要
let kinds = [WhiteCat(),BlackCat(),WhiteCat(),WhiteCat()]
for item in kinds {
    if item is WhiteCat {
        print("白貓")//!< 3次
    }
    if item is BlackCat {
        print("黑貓")//!< 1次
    }
    if item is Cat {
        print("貓")//!< 4次
    }
}

向下轉(zhuǎn)換

某個(gè)類(lèi)類(lèi)型的常量或變量實(shí)際上可能是其子類(lèi)的實(shí)例而账。這種情況下,我們會(huì)用到類(lèi)型轉(zhuǎn)換操作符(as因篇?as泞辐!)向下轉(zhuǎn)換為子類(lèi)類(lèi)型。
as?:類(lèi)型轉(zhuǎn)換的條件形式竞滓,向下轉(zhuǎn)換為某個(gè)類(lèi)型時(shí)铛碑,返回該類(lèi)型的可選值,即:轉(zhuǎn)換失敗時(shí)返回nil虽界。使用場(chǎng)景:向下轉(zhuǎn)換可能會(huì)失敗的情況汽烦。
as!:類(lèi)型轉(zhuǎn)換的強(qiáng)制形式,向下轉(zhuǎn)換為某個(gè)類(lèi)型時(shí)莉御,會(huì)進(jìn)行強(qiáng)制解包撇吞,即:轉(zhuǎn)換失敗時(shí)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。使用場(chǎng)景:向下轉(zhuǎn)換確定不會(huì)失敗

//必須符合`Cat`類(lèi)以及其子類(lèi)礁叔,類(lèi)型推斷需要
let kinds = [WhiteCat(),BlackCat(),WhiteCat(),WhiteCat()]
for item in kinds {
    if let white = item as? WhiteCat {
        print("毛發(fā):\(white.hairColor())")
    }
    if let black = item as? BlackCat {
        print("毛發(fā):\(black.hairColor())")
    }
}

下述內(nèi)容總結(jié)自 蘋(píng)果官方博客:
Swift 1.2之前as運(yùn)算符可以執(zhí)行兩種不同類(lèi)型的轉(zhuǎn)換:保證轉(zhuǎn)換和強(qiáng)制轉(zhuǎn)換牍颈。
保證轉(zhuǎn)換:保證將一種類(lèi)型的值轉(zhuǎn)換為另一種類(lèi)型,這種保證由編譯器編譯時(shí)驗(yàn)證琅关。
例如:
? 向上轉(zhuǎn)換(Upcasting)煮岁,將當(dāng)前類(lèi)型的值轉(zhuǎn)換為該類(lèi)型的父類(lèi)之一。
? 指定數(shù)字的類(lèi)型:let num = 6 as Float
強(qiáng)制轉(zhuǎn)換:強(qiáng)制將一種類(lèi)型的值轉(zhuǎn)換為另一種類(lèi)型涣易,這種轉(zhuǎn)換編譯器無(wú)法保證安全性画机,并且可能觸發(fā)運(yùn)行時(shí)錯(cuò)誤。
例如:上述的向下轉(zhuǎn)換(Downcasting),將一種類(lèi)型的值轉(zhuǎn)換為其子類(lèi)之一新症。
在Swift 1.2之后保證轉(zhuǎn)換仍然使用as操作符步氏,但強(qiáng)制轉(zhuǎn)換使用as!操作符。

AnyAnyObject的類(lèi)型轉(zhuǎn)換

Swift提供了兩種特殊類(lèi)型來(lái)處理非特定類(lèi)型:

  • any :可以表示任何類(lèi)型的實(shí)例徒爹,包括函數(shù)類(lèi)型荚醒。
  • AnyObject :可以表示任何類(lèi)類(lèi)型的實(shí)例。

在某些使用anyAnyObject的特殊場(chǎng)景下隆嗅,對(duì)于AnyAnyObject表示的實(shí)例界阁,需要運(yùn)用類(lèi)型轉(zhuǎn)換模式,值綁定模式胖喳,表達(dá)式模式等模式匹配的知識(shí)泡躯。所以我們先介紹下Swift中的模式。

類(lèi)型轉(zhuǎn)換模式

類(lèi)型轉(zhuǎn)換有兩種模式:is模式和as模式。is模式僅在switch語(yǔ)句的case標(biāo)簽中使用精续。is模式和as模式有如下形式:

is <#Type#>
//pattern:代表此處也需要一個(gè)模式
<#pattern#> as <#Type#>

is模式:如果運(yùn)行時(shí)值的類(lèi)型與is模式右側(cè)指定的類(lèi)型或該類(lèi)型的子類(lèi)相同,則is模式會(huì)匹配到這個(gè)值粹懒。此行為很適用switch語(yǔ)句的case場(chǎng)景重付。is模式的行為類(lèi)似于is運(yùn)算符,因?yàn)樗鼈兌紙?zhí)行類(lèi)型轉(zhuǎn)換但類(lèi)型轉(zhuǎn)換后丟棄了返回的類(lèi)型凫乖。

as模式:如果在運(yùn)行時(shí)值的類(lèi)型與as模式右側(cè)指定的類(lèi)型或該類(lèi)型的子類(lèi)相同确垫,則as模式會(huì)匹配到這個(gè)值。如果匹配成功帽芽,則會(huì)將匹配到的值的類(lèi)型將轉(zhuǎn)換為as模式右側(cè)指定的類(lèi)型删掀。

值綁定模式

值綁定模式將匹配到的值綁定到變量或常量。
將匹配到的值綁定到常量导街,綁定模式以let關(guān)鍵字開(kāi)頭披泪;綁定到變量以var關(guān)鍵字開(kāi)頭。

let point = (3,2)
switch point {
case let(x,y):
    //值綁定模式匹配到的X值:3,Y值:2
    print("值綁定模式匹配到的X值:\(x),Y值:\(y)")
}

通配符模式

通配符模式匹配并忽略任何值搬瑰,并由下劃線_表示款票。

for _ in 1...9 {
    print("通配符模式")
}

標(biāo)識(shí)符模式

標(biāo)識(shí)符模式匹配任何值,并將匹配的值綁定到變量或常量的名稱(chēng)泽论。

let someValue = 42

someValue是一個(gè)與Int類(lèi)型的值42匹配的標(biāo)識(shí)符模式艾少。匹配成功,42將被賦值給常量someValue翼悴。
當(dāng)變量或常量聲明的左側(cè)的模式是標(biāo)識(shí)符模式時(shí)缚够,標(biāo)識(shí)符模式隱式地是值綁定模式的子模式。

元組模式

元組模式是以逗號(hào)分隔的零個(gè)或多個(gè)元素列表鹦赎,括在括號(hào)中谍椅。元組模式匹配相應(yīng)元組類(lèi)型的值。
包含單個(gè)元素的元組模式周?chē)睦ㄌ?hào)無(wú)效古话。該模式匹配該單個(gè)元素類(lèi)型的值毯辅。所以下面寫(xiě)法是等效的:

let a = 2        // a: Int = 2
let (a) = 2      // a: Int = 2
let (a): Int = 2 // a: Int = 2

枚舉Case模式

枚舉Case模式匹配現(xiàn)有枚舉中存在case。枚舉Case模式出現(xiàn)在switch語(yǔ)句的case標(biāo)簽中以及if煞额,while思恐, guardfor-in 語(yǔ)句中膊毁。
如果嘗試匹配的枚舉case具有關(guān)聯(lián)值胀莹,則相應(yīng)的枚舉Case模式必須指定與每個(gè)關(guān)聯(lián)值對(duì)應(yīng)的元組。

enum VendingMachineError {
    case InvalidGoods//!< 商品無(wú)效
    case StockInsufficient//!< 庫(kù)存不足
    case CoinInsufficient(coinNeeded:Int,caseDes:String)
}
let enumArray = [VendingMachineType.CoinInsufficient(coinNeeded: 4, caseDes: "自動(dòng)售貨機(jī)婚温,硬幣不足描焰,請(qǐng)補(bǔ)充"),
                 .InvalidGoods,
                 .StockInsufficient,
                 .CoinInsufficient(coinNeeded: 6, caseDes: "自動(dòng)售貨機(jī),硬幣不足,超過(guò)限額")]
for patternCase in enumArray {
    switch patternCase {
    case .CoinInsufficient(coinNeeded: let x, caseDes: let y) where x > 5:
        print(x,y)
    case let .CoinInsufficient(coinNeeded: x, caseDes: y):
        print(x,y)
    case .InvalidGoods:
        print("商品無(wú)效")
    default:
        print("未匹配到")
    }
}

枚舉Case模式還匹配枚舉類(lèi)型的可選項(xiàng)荆秦。當(dāng)可選項(xiàng)Optional是枚舉類(lèi)型時(shí)篱竭,.none.some 能夠作為枚舉類(lèi)型的其他case出現(xiàn)在同一個(gè)switch語(yǔ)句中。這種簡(jiǎn)化的語(yǔ)法允許我們省略可選模式步绸。

enum SomeEnum { case left, right,top,down}
let array : Array<SomeEnum?> = [.left,nil,.right,.top,.down]
//方式一:
array.forEach { (item) in
    switch item {
    case .left?:
        print("左")
    case SomeEnum.right?:
        print("右")
    case .down?:
        print("下")
    case .top?:
        print("上")
    default:
        print("沒(méi)有值")
    }
}
//方式二:
array.forEach { (item) in
    switch item {
    case .some(let x):
        print("對(duì)可選項(xiàng)item進(jìn)行解包得到:\(x)")//!< left,right,top,down
    case .none:
        print("沒(méi)有值") //nil
    }
}

可選模式

可選模式匹配包含在Optional<Wrapped>枚舉(這是可選項(xiàng)的實(shí)現(xiàn)原理)對(duì)應(yīng)的case項(xiàng):some(Wrapped)中的值掺逼。即匹配可選項(xiàng)有值的情況。

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    /// The absence of a value.
    /// In code, the absence of a value is typically written using the `nil`
    /// literal rather than the explicit `.none` enumeration case.
    case none
    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)
    ......
}

可選模式由標(biāo)識(shí)符模式組成后面緊跟?并出現(xiàn)在與枚舉Case模式相同的位置瓤介。
因?yàn)榭蛇x模式是Optional<Wrapped>枚舉的Case模式語(yǔ)法糖吕喘。所以下面兩種寫(xiě)法是等效的:

let someInt : Int? = 42
//方式一:枚舉case模式
if case let .some(x) = someInt {
    print(x)
}
if case  .some(let x) = someInt {
    print(x)
}
//方式二:可選模式
if case let x? = someInt {
    print(x)
}

使用可選模式迭代包含可選項(xiàng)的數(shù)組是很方便的:

enum SomeEnum { case left, right,top,down}
let array : Array<SomeEnum?> = [.left,nil,.right,nil,.top,.down]
for case let item? in array {
    print(item)//!< log:left right top down
}
for case let .some(item) in array {
    print(item)//!< log:left right top down
}
for case .some(let item) in array {
    print(item)//!< log:left right top down
}

表達(dá)式模式

表達(dá)式模式:表示表達(dá)式的值,僅出現(xiàn)在switch語(yǔ)句的case標(biāo)簽中刑桑。
表達(dá)式模式的機(jī)制:使用Swift標(biāo)準(zhǔn)庫(kù)中的~=操作符將表達(dá)式模式中表達(dá)式的值與匹配值(輸入值)進(jìn)行比較氯质,若~=返回true則證明匹配成功,否則匹配失敗祠斧。
~=運(yùn)算符默認(rèn)情況下使用==運(yùn)算符比較兩個(gè)相同類(lèi)型的值闻察;也可以通過(guò)檢查某個(gè)值是否在某個(gè)范圍內(nèi)來(lái)匹配范圍值。

let point = (9,14)
switch point {
case (9,14):
    print("表達(dá)式模式使用`~=`精準(zhǔn)匹配::(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達(dá)式模式使用`~=`范圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}

可以重載?=運(yùn)算符提供自定義表達(dá)式匹配行為:

//全局聲明:class外部琢锋,否則報(bào)錯(cuò)
func ~= (pattern: String, value: Int) -> Bool {
    return pattern == "\(value)"
}
let point = (9,14)
switch point {
case ("9","14")://若不重載則會(huì)報(bào)錯(cuò)
    print("表達(dá)式模式使用`~=`精準(zhǔn)匹配:(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達(dá)式模式使用`~=`范圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}

介紹完模式蜓陌,接下來(lái)我們舉例來(lái)說(shuō)明模式在AnyAnyObject的類(lèi)型轉(zhuǎn)換的使用。
示例一:

var things : [Any] = [0, 0.0, 42, 3.14159, "hello", (3.0, 5.0),
                      WhiteCat(),{ (name: String) -> String in "Hello, \(name)" } ]
for thing in things {
    switch thing {
    case 0 as Int:
        print("`as`模式匹配兩部分吩蔑,pattern:表達(dá)式模式(`0`)钮热,type:匹配類(lèi)型(`Int`),匹配結(jié)果:0")
    case (0) as Double:
        print("`as`模式匹配兩部分,pattern:表達(dá)式模式(`0`)烛芬,type:匹配類(lèi)型(`Double`),匹配結(jié)果:0.0")
    case is Double:
        print("`is`模式匹配`Double`類(lèi)型的值隧期,值類(lèi)型與`is`右側(cè)類(lèi)型及子類(lèi)相同時(shí),執(zhí)行此句")
    case let someInt as Int:
        print("`as`模式匹配兩部分赘娄,pattern:值綁定模式(`let someInt`)仆潮,type:匹配類(lèi)型(`Int`),匹配結(jié)果:\(someInt)")
    case _ as Int:
        print("`as`模式匹配兩部分,pattern:通配符模式(`_`)遣臼,type:匹配類(lèi)型(`Int`),匹配結(jié)果被忽略")
    case let someDouble as Double where someDouble > 0:
        print("`as`模式匹配兩部分性置,pattern:值綁定模式(`let someDouble`),type:匹配類(lèi)型(`Double`),匹配結(jié)果:\(someDouble)")
    case let someString as String:
        print("`as`模式匹配兩部分揍堰,pattern:值綁定模式(`let someString`)鹏浅,type:匹配類(lèi)型(`String`),匹配結(jié)果:\(someString)")
    case let (x, y) as (Double, Double):
        print("`as`模式匹配兩部分,pattern:元組模式(`let (x, y) `)屏歹,type:匹配類(lèi)型(元組`(Double, Double)`),匹配結(jié)果:\((x, y))")
        fallthrough
    case (2.0...4.0, 3.0...6.0) as (Double, Double):
        print("`as`模式匹配兩部分隐砸,pattern:表達(dá)式模式(`(2.0...4.0, 3.0...6.0) `),type:匹配類(lèi)型(元組`(Double, Double)`))")
    case let cat as WhiteCat:
        print("`as`模式匹配兩部分蝙眶,pattern:值綁定模式(`let cat`)季希,type:匹配類(lèi)型(對(duì)象`WhiteCat`),匹配結(jié)果:\(cat)")
    case let sayHelloFunc as (String) -> String:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let sayHelloFunc`),type:匹配類(lèi)型(函數(shù)`(String) -> String`),匹配結(jié)果:\(sayHelloFunc("QiShare"))")
    default:
        print("其他結(jié)果式塌,未匹配到")
    }
}

示例二:

let point = (9,14)
switch point {
case (9,14):
    print("表達(dá)式模式使用`~=`精準(zhǔn)匹配::(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達(dá)式模式使用`~=`范圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}

參考資料:
swift 5.1官方編程指南

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末博敬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子峰尝,更是在濱河造成了極大的恐慌偏窝,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件境析,死亡現(xiàn)場(chǎng)離奇詭異囚枪,居然都是意外死亡派诬,警方通過(guò)查閱死者的電腦和手機(jī)劳淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)默赂,“玉大人沛鸵,你說(shuō)我怎么就攤上這事±掳耍” “怎么了曲掰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奈辰。 經(jīng)常有香客問(wèn)我栏妖,道長(zhǎng),這世上最難降的妖魔是什么奖恰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任吊趾,我火速辦了婚禮,結(jié)果婚禮上瑟啃,老公的妹妹穿的比我還像新娘论泛。我一直安慰自己,他們只是感情好蛹屿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布屁奏。 她就那樣靜靜地躺著,像睡著了一般错负。 火紅的嫁衣襯著肌膚如雪坟瓢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天犹撒,我揣著相機(jī)與錄音载绿,去河邊找鬼。 笑死油航,一個(gè)胖子當(dāng)著我的面吹牛崭庸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怕享,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼执赡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起函筋,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沙合,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后跌帐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體首懈,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年谨敛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了究履。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脸狸,死狀恐怖最仑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炊甲,我是刑警寧澤泥彤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站卿啡,受9級(jí)特大地震影響吟吝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜颈娜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一剑逃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揭鳞,春花似錦炕贵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至乓梨,卻和暖如春鳖轰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扶镀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蕴侣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人臭觉。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓昆雀,卻偏偏與公主長(zhǎng)得像辱志,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狞膘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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