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

 更新时间:2020年6月30日 23:37  点击:1394

类型转换在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 类型转换与模式匹配内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • 浅析C#数据类型转换的几种形式

    本篇文章是对C#中数据类型转换的几种形式进行了详细的分析介绍,需要的朋友参考下...2020-06-25
  • c#入门之类型转换详解

    这篇文章主要介绍了c#的类型转换详解,类型转换分两种形式:隐式转换、显示转换,下面是详细介绍...2020-06-25
  • swift中利用runtime交换方法的实现示例

    这篇文章主要给大家介绍了关于swift中利用runtime交换方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。...2020-06-30
  • c#之用户定义的数据类型转换介绍

    c#允许定义自己的数据类型,这意味着需要某些工具支持在自己的数据类型间进行数据转换。方法是把数据类型转换定义为相关类的一个成员运算符,数据类型转换必须声明是隐式或者显式,以说明怎么使用它...2020-06-25
  • Swift设置UILabel内边距的实例代码

    有时候,我们需要一个显示文字,又想这些文字与边界之间有自定义的边距,所以下面这篇文章主要给大家介绍了关于Swift设置UILabel内边距的相关资料,需要的朋友可以参考下...2021-10-14
  • swift中的@UIApplicationMain示例详解

    这篇文章主要给大家介绍了关于swift中@UIApplicationMain的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。...2020-06-30
  • Swift实现多个TableView侧滑与切换效果

    这篇文章主要为大家详细介绍了Swift实现多个TableView侧滑与切换效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-30
  • 详解swift中xcworkspace多项目管理

    给大家详细讲解了IOS开发中swift语言xcworkspace多项目管理的方法和介绍,一起参考一下。...2020-06-30
  • Swift 中如何使用 Option Pattern 改善可选项的 API 设计

    这篇文章主要介绍了Swift 中如何使用 Option Pattern 改善可选项的 API 设计,帮助大家更好的进行ios开发,感兴趣的朋友可以了解下...2020-10-23
  • 如何使用Swift来实现一个命令行工具的方法

    这篇文章主要介绍了如何使用Swift来实现一个命令行工具,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
  • Swift中的命名空间详解

    这篇文章主要给大家介绍了关于Swift中命名空间的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
  • 浅析Swift中struct与class的区别(汇编角度底层分析)

    这篇文章主要介绍了Swift中struct与class的区别 ,本文从汇编角度分析struct与class的区别,通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-06-30
  • Swift缩放并填充图片功能的实现

    最近有一个需求,就是将图片先等比例缩放到指定大小,然后将空余出来空间填充为黑色,返回指定大小的图片。本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2021-11-02
  • OpenResty中正则模式匹配的2种方法详解

    在 OpenResty 中,同时存在两套正则表达式规范:Lua 语言的规范和 Nginx 的规范,下面这篇文章主要给大家介绍了关于OpenResty中正则模式匹配的2种方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。...2020-06-30
  • Swift中优雅处理闭包导致的循环引用详解

    这篇文章主要给大家介绍了关于Swift中优雅的处理闭包导致的循环引用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Swift具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-30
  • Swift实现倒计时5秒功能

    这篇文章主要为大家详细介绍了Swift实现倒计时5秒功能,在“登录”和“注册”页面也有相似功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-30
  • Swift 使用 Observe 监测页面滚动的实现方法

    这篇文章主要介绍了Swift 使用 Observe 监测页面滚动的实现方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-06-30
  • Swift在控件中添加点击手势的方法

    这篇文章主要介绍了Swift在控件中添加点击手势的方法,本文讲解如何在tableview的headerview中添加点击手势的方法,需要的朋友可以参考下...2020-06-30
  • Swift如何使用类型擦除及自定义详解

    有很多地方会用到类型擦除,并且它们的作用的各不相同。下面这篇文章主要给大家介绍了关于Swift如何使用类型擦除及自定义的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
  • Swift利用CoreData如何存储多种数据类的通讯录

    这篇文章主要给大家介绍了关于Swift利用CoreData如何存储多种数据类的通讯录的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧。...2020-06-30