首页 > 编程语言 > Swift 5.1 之类型转换与模式匹配的教程详解
2020
09-30

Swift 5.1 之类型转换与模式匹配的教程详解

类型转换在Swift中使用 isas 操作符实现。

类型检查

使用操作符 is 检查一个实例是否是某个确定的类以及其继承体系的父类或子类类型。如果是某个确定的类(该类继承体系的父类或子类)类型,则返回 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`类以及其子类,类型推断需要
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! 操作符。

AnyAnyObject 的类型转换

Swift提供了两种特殊类型来处理非特定类型:

any
AnyObject

在某些使用 anyAnyObject 的特殊场景下,对于 AnyAnyObject 表示的实例,需要运用类型转换模式,值绑定模式,表达式模式等模式匹配的知识。所以我们先介绍下Swift中的模式。

类型转换模式

类型转换有两种模式: is 模式和 as 模式。 is 模式仅在 switch 语句的 case 标签中使用。 is 模式和 as 模式有如下形式:

is <#Type#>
//pattern:代表此处也需要一个模式
<#pattern#> as <#Type#>

is 模式: 如果运行时值的类型与 is 模式右侧指定的类型或该类型的子类相同,则 is 模式会匹配到这个值。此行为很适用 switch 语句的 case 场景。 is 模式的行为类似于 is 运算符,因为它们都执行类型转换但类型转换后丢弃了返回的类型。

as 模式: 如果在运行时值的类型与 as 模式右侧指定的类型或该类型的子类相同,则 as 模式会匹配到这个值。如果匹配成功,则会将匹配到的值的类型将转换为 as 模式右侧指定的类型。

值绑定模式

值绑定模式将匹配到的值绑定到变量或常量。 将匹配到的值绑定到常量,绑定模式以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标签中以及 if , while ,  guard , for - 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("未匹配")
}

介绍完模式,接下来我们举例来说明模式在 Any 和 AnyObject 的类型转换的使用。 示例一:

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官方编程指南

总结

到此这篇关于Swift 5.1 之类型转换与模式匹配的教程详解的文章就介绍到这了,更多相关Swift 类型转换与模式匹配内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!

编程技巧