Swift 5.1 (17) - 類型轉換與模式匹配

級別: ★☆☆☆☆
標籤:「iOS」「Swift 5.1 」「is」「as」「pattern」「any」「anyObject」
做者: 沐靈洛
審校: QiShare團隊php


類型轉換在Swift中使用isas操做符實現。git

類型檢查

使用操做符is檢查一個實例是不是某個肯定的類以及其繼承體系的父類或子類類型。若是是某個肯定的類(該類繼承體系的父類或子類)類型,則返回true,不然返回falsegithub

class Cat {
    func hairColor() -> String {
        return "五光十色"
    }
}
class WhiteCat: Cat {
    override func hairColor() -> String {
        return "白色"
    }
}
class BlackCat: Cat {
    override func hairColor() -> String {
        return "黑色"
    }
}
//必須符合`Cat`類以及其子類,類型推斷須要
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次
    }
}
複製代碼

向下轉換

某個類類型的常量或變量實際上多是其子類的實例。這種狀況下,咱們會用到類型轉換操做符(as?as!)向下轉換爲子類類型。
as?:類型轉換的條件形式,向下轉換爲某個類型時,返回該類型的可選值,即:轉換失敗時返回nil。使用場景:向下轉換可能會失敗的狀況。
as!:類型轉換的強制形式,向下轉換爲某個類型時,會進行強制解包,即:轉換失敗時觸發運行時錯誤。使用場景:向下轉換肯定不會失敗編程

//必須符合`Cat`類以及其子類,類型推斷須要
let kinds = [WhiteCat(),BlackCat(),WhiteCat(),WhiteCat()]
for item in kinds {
    if let white = item as? WhiteCat {
        print("毛髮:\(white.hairColor())")
    }
    if let black = item as? BlackCat {
        print("毛髮:\(black.hairColor())")
    }
}
複製代碼

下述內容總結自 蘋果官方博客:
Swift 1.2以前as運算符能夠執行兩種不一樣類型的轉換:保證轉換和強制轉換。
保證轉換: 保證將一種類型的值轉換爲另外一種類型,這種保證由編譯器編譯時驗證。
例如:
• 向上轉換(Upcasting),將當前類型的值轉換爲該類型的父類之一。
• 指定數字的類型:let num = 6 as Float
**強制轉換:**強制將一種類型的值轉換爲另外一種類型,這種轉換編譯器沒法保證安全性,而且可能觸發運行時錯誤。
例如:上述的向下轉換(Downcasting),將一種類型的值轉換爲其子類之一。 在Swift 1.2以後保證轉換仍然使用as操做符,但強制轉換使用as!操做符。swift

AnyAnyObject的類型轉換

Swift提供了兩種特殊類型來處理非特定類型:數組

  • any :能夠表示任何類型的實例,包括函數類型。
  • AnyObject :能夠表示任何類類型的實例。

在某些使用anyAnyObject的特殊場景下,對於AnyAnyObject表示的實例,須要運用類型轉換模式,值綁定模式,表達式模式等模式匹配的知識。因此咱們先介紹下Swift中的模式。安全

類型轉換模式

類型轉換有兩種模式:is模式和as模式。is模式僅在switch語句的case標籤中使用。is模式和as模式有以下形式:bash

is <#Type#>
//pattern:表明此處也須要一個模式
<#pattern#> as <#Type#>
複製代碼

is模式: 若是運行時值的類型與is模式右側指定的類型或該類型的子類相同,則is模式會匹配到這個值。此行爲很適用switch語句的case場景。is模式的行爲相似於is運算符,由於它們都執行類型轉換但類型轉換後丟棄了返回的類型。微信

as模式: 若是在運行時值的類型與as模式右側指定的類型或該類型的子類相同,則as模式會匹配到這個值。若是匹配成功,則會將匹配到的值的類型將轉換爲as模式右側指定的類型。app

值綁定模式

值綁定模式將匹配到的值綁定到變量或常量。 將匹配到的值綁定到常量,綁定模式以let關鍵字開頭;綁定到變量以var關鍵字開頭。

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

通配符模式

通配符模式匹配並忽略任何值,並由下劃線_表示。

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

標識符模式

標識符模式匹配任何值,並將匹配的值綁定到變量或常量的名稱。

let someValue = 42
複製代碼

someValue是一個與Int類型的值42匹配的標識符模式。匹配成功,42將被賦值給常量someValue。 當變量或常量聲明的左側的模式是標識符模式時,標識符模式隱式地是值綁定模式的子模式。 ####元組模式 元組模式是以逗號分隔的零個或多個元素列表,括在括號中。元組模式匹配相應元組類型的值。
包含單個元素的元組模式周圍的括號無效。該模式匹配該單個元素類型的值。因此下面寫法是等效的:

let a = 2        // a: Int = 2
let (a) = 2      // a: Int = 2
let (a): Int = 2 // a: Int = 2
複製代碼

枚舉Case模式

枚舉Case模式匹配現有枚舉中存在case。枚舉Case模式出如今switch語句的case標籤中以及ifwhile, guardfor-in 語句中。
若是嘗試匹配的枚舉case具備關聯值,則相應的枚舉Case模式必須指定與每一個關聯值對應的元組。

enum VendingMachineError {
    case InvalidGoods//!< 商品無效
    case StockInsufficient//!< 庫存不足
    case CoinInsufficient(coinNeeded:Int,caseDes:String)
}
let enumArray = [VendingMachineType.CoinInsufficient(coinNeeded: 4, caseDes: "自動售貨機,硬幣不足,請補充"),
                 .InvalidGoods,
                 .StockInsufficient,
                 .CoinInsufficient(coinNeeded: 6, caseDes: "自動售貨機,硬幣不足,超過限額")]
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("商品無效")
    default:
        print("未匹配到")
    }
}
複製代碼

枚舉Case模式還匹配枚舉類型的可選項。當可選項Optional是枚舉類型時,.none 和.some 可以做爲枚舉類型的其餘case出如今同一個switch語句中。這種簡化的語法容許咱們省略可選模式。

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("沒有值")
    }
}
//方式二:
array.forEach { (item) in
    switch item {
    case .some(let x):
        print("對可選項item進行解包獲得:\(x)")//!< left,right,top,down
    case .none:
        print("沒有值") //nil
    }
}
複製代碼

可選模式

可選模式匹配包含在Optional<Wrapped>枚舉(這是可選項的實現原理)對應的case項:some(Wrapped)中的值。即匹配可選項有值的狀況。

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)
    ......
}
複製代碼

可選模式由標識符模式組成後面緊跟?並出如今與枚舉Case模式相同的位置。 由於可選模式是Optional<Wrapped>枚舉的Case模式語法糖。因此下面兩種寫法是等效的:

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)
}
複製代碼

使用可選模式迭代包含可選項的數組是很方便的:

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
}
複製代碼

表達式模式

表達式模式:表示表達式的值,僅出如今switch語句的case標籤中。
表達式模式的機制:使用Swift標準庫中的~=操做符將表達式模式中表達式的值與匹配值(輸入值)進行比較,若~=返回true則證實匹配成功,不然匹配失敗。
~=運算符默認狀況下使用==運算符比較兩個相同類型的值;也能夠經過檢查某個值是否在某個範圍內來匹配範圍值。

let point = (9,14)
switch point {
case (9,14):
    print("表達式模式使用`~=`精準匹配::(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達式模式使用`~=`範圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}
複製代碼

能夠重載〜=運算符提供自定義表達式匹配行爲:

//全局聲明:class外部,不然報錯
func ~= (pattern: String, value: Int) -> Bool {
    return pattern == "\(value)"
}
let point = (9,14)
switch point {
case ("9","14")://若不重載則會報錯
    print("表達式模式使用`~=`精準匹配:(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達式模式使用`~=`範圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}
複製代碼

介紹完模式,接下來咱們舉例來講明模式在AnyAnyObject的類型轉換的使用。 示例一:

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:表達式模式(`0`),type:匹配類型(`Int`),匹配結果:0")
    case (0) as Double:
        print("`as`模式匹配兩部分,pattern:表達式模式(`0`),type:匹配類型(`Double`),匹配結果:0.0")
    case is Double:
        print("`is`模式匹配`Double`類型的值,值類型與`is`右側類型及子類相同時,執行此句")
    case let someInt as Int:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let someInt`),type:匹配類型(`Int`),匹配結果:\(someInt)")
    case _ as Int:
        print("`as`模式匹配兩部分,pattern:通配符模式(`_`),type:匹配類型(`Int`),匹配結果被忽略")
    case let someDouble as Double where someDouble > 0:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let someDouble`),type:匹配類型(`Double`),匹配結果:\(someDouble)")
    case let someString as String:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let someString`),type:匹配類型(`String`),匹配結果:\(someString)")
    case let (x, y) as (Double, Double):
        print("`as`模式匹配兩部分,pattern:元組模式(`let (x, y) `),type:匹配類型(元組`(Double, Double)`),匹配結果:\((x, y))")
        fallthrough
    case (2.0...4.0, 3.0...6.0) as (Double, Double):
        print("`as`模式匹配兩部分,pattern:表達式模式(`(2.0...4.0, 3.0...6.0) `),type:匹配類型(元組`(Double, Double)`))")
    case let cat as WhiteCat:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let cat`),type:匹配類型(對象`WhiteCat`),匹配結果:\(cat)")
    case let sayHelloFunc as (String) -> String:
        print("`as`模式匹配兩部分,pattern:值綁定模式(`let sayHelloFunc`),type:匹配類型(函數`(String) -> String`),匹配結果:\(sayHelloFunc("QiShare"))")
    default:
        print("其餘結果,未匹配到")
    }
}
複製代碼

示例二:

let point = (9,14)
switch point {
case (9,14):
    print("表達式模式使用`~=`精準匹配::(\(point.0),\(point.1))")
    fallthrough
case (5..<10,0...20):
    print("表達式模式使用`~=`範圍匹配:(\(point.0),\(point.1))")
default:
    print("未匹配")
}
複製代碼

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


瞭解更多iOS及相關新技術,請關注咱們的公衆號:

image

小編微信:可加並拉入《QiShare技術交流羣》。

image

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
淺談編譯過程
深刻理解HTTPS 淺談 GPU 及 「App渲染流程」
iOS 查看及導出項目運行日誌
Flutter Platform Channel 使用與源碼分析
開發沒切圖怎麼辦?矢量圖標(iconFont)上手指南
DarkMode、WKWebView、蘋果登陸是否必須適配?
奇舞團安卓團隊——aTaller
奇舞週刊

相關文章
相關標籤/搜索