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

(0)

相关推荐

  • 深入讲解Swift中的模式匹配

    模式匹配 模式匹配是 Swift 中非常常见的一种编程模式,使用模式匹配,可以帮助我们写出简明.清晰以及易读的代码,使我们的代码变得简洁而强大. 条件判断中的模式匹配 条件判断是我们使用最普遍的流程控制,在 Swift 中,只能接受 Bool 类型的值作为条件体:除了直接判断 Bool 值之外,我们还能使用使用条件语句进行可选绑定,这在我们开发中是非常常用的方式. 匹配枚举值 在 Swift 中,创建的枚举类型默认是不可比较的(没有实现Comparable协议),这就意味着我们不能直接使用==操

  • 详解Swift中的数据类型类型转换

    一.类型检查与转换 在Objective-C和Java中,任何类型实例都可以通过强转使编译器认为它是另一种类型的实例,这么做其实是将所有的安全检查工作都交给了开发者自己来做.先比之下,Swift中的Optional类型转换就会比较安全与可靠. Swift中使用is关键字来进行类型的检查,其会返回一个布尔值true或者false来表明检查是否成立,示例如下: var str = "HS" if str is String { print(str) } Swift中有向上兼容与向下转换的特

  • Swift中switch语句区间和元组模式匹配

    废话不多说了,下面一段代码给大家介绍了switch语句区间和元组模式匹配,具体内容如下所示: // switch 的广义匹配 let x = 1000 // 也就是说并没有像C语言那样 要求 switch 后面的是整数常量 switch x { // case后面可以跟区间啦 case 1...9: print("个位数") case 10...99: print("十位数") case 100...999: print("百位数") case

  • Swift编程中的一些类型转换方法详解

    验证一个实例的类型'类型转换'在 Swift 语言编程中.它是用来检查实例类型是否属于特定超类或子类或其自己的层次结构定义. Swift 类型转换提供两个操作符:"is" 检查值的类型和 'as' 将类型值转换为不同的类型值. 类型转换还检查实例类型是否符合特定的协议一致性标准. 定义一个类层次结构 类型转换用于检查实例的类型或者它属于特定类型.此外,检查类和它的子类层次结构来检查并转换这些实例,使之作为一个相同的层次结构. 复制代码 代码如下: class Subjects {   

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

    类型转换在Swift中使用 is 和 as 操作符实现. 类型检查 使用操作符 is 检查一个实例是否是某个确定的类以及其继承体系的父类或子类类型.如果是某个确定的类(该类继承体系的父类或子类)类型,则返回 true ,否则返回 false . class Cat { func hairColor() -> String { return "五颜六色" } } class WhiteCat: Cat { override func hairColor() -> String

  • golang类型转换组件Cast的使用详解

    开源地址 https://github.com/spf13/cast Cast是什么? Cast是一个库,以一致和简单的方式在不同的go类型之间转换. Cast提供了简单的函数,可以轻松地将数字转换为字符串,将接口转换为bool类型等等.当一个明显的转换是可能的时,Cast会智能地执行这一操作.它不会试图猜测你的意思,例如,你只能将一个字符串转换为int的字符串表示形式,例如"8".Cast是为Hugo开发的,Hugo是一个使用YAML.TOML或JSON作为元数据的网站引擎. 为什么

  • Swift中的高阶函数功能作用示例详解

    目录 高阶函数的作用 1. 简化代码 2. 提高可读性 3. 支持函数式编程 4. 提高代码的可重用性 常见的高阶函数 1. map() 2. filter() 3. reduce() 4. sorted() 5. forEach() 6. compactMap() 7. flatMap() 8. zip() 9. first() 10. contains() 高阶函数的作用 Swift中的高阶函数是指那些参数或返回值是函数的函数.它们的存在使得我们可以用非常简洁和优雅的代码来解决许多问题. 1

  • Swift流程控制之循环语句和判断语句详解

    Swift提供了所有c类语言的控制流结构.包括for和while循环来执行一个任务多次:if和switch语句来执行确定的条件下不同的分支的代码:break和continue关键字能将运行流程转到你代码的另一个点上. 除了C语言传统的for-condition-increment循环,Swift加入了for-in循环,能更加容易的遍历arrays, dictionaries, ranges, strings等其他序列类型. Swift的switch语句也比C语言的要强大很多. Swift中swi

  • Swift中常量和变量的区别与声明详解

    Swift是弱类型语言吗? 答案是否定的,Swift 是强类型语言,下面上一个栗子 上面代码中报错了,报的是不能指定 Int 类型为 String 类型. 这里要注意一下在 Swift 中的整形是I,而字符类型首字母是S,都是大写字母 在 Swift 中我们可以直接声明 var 类型变量,可以不直接指定其类型,这是Swift语言的一种机制,当我们声明一个变量的初始值后,就已经确定这个变量是什么类型,Type Inference (类型推断) 如何查看一个变量的类型 在开发中我们一般如何查看一个变

  • Swift 3.1聊天界面键盘效果的实现详解

    前言 最近写的 Swift 项目里要实现一个聊天界面,在处理键盘弹出的时候遇到了一点麻烦. 麻烦就在于键盘弹出后如何处理屏幕和键盘的关系 经过一番死磕,终于做出了想要的效果,效果如下: 注:原本项目是 Swift 2.3 写的,为了写这篇博客,用 Swift 3.1 重新实现了一遍. 感受:方法名真的缩短了不少,

  • Swift读取App的版本信息与PCH文件详解

    Swift读取App版本信息 有时间我们需要读取APP的版本信息之类的配置信息,这样的我们可以使用方法:NSBundle.mainBundle().infoDictionary 来获取 Example: let infoDictionary = NSBundle.mainBundle().infoDictionary let shortVersion = infoDictionary!["CFBundleShortVersionString"] print(shortVersion!)

  • Swift中非可选的可选值类型处理方法详解

    前言 在我们使用objective-c表示字符串信息的时候,可以用下面方法书写. NSString *str = @"秋恨雪"; str = nil; 因为objective-c是弱类型语言,所以这里的str既可以是具体的字符串也可以是nil.但到了Swift中就不可以了,因为Swift是类型安全的语言,一个String类型的变量不可能既能是具体的字符串,又可以为nil(更严格的说String类型的内容只能是字符串).所以,在Swift中有了可选类型的概念.(其实这一概念也是"

  • swift中AnyObject和Any的介绍与区别详解

    诞生 swift 作为新起步的语言,必然抛不掉一些历史遗留包袱.用过 Objective-C 的同学肯定知道有一种叫做 id 的类型.他可以表示任意类的实例,编译器不会对其类型声明的变量进行检查.在用 swift 做 app 开发时,为了能适配 Cocoa 架构,AnyObject 就诞生了.它可以代表任意 class 类型(用来替代OC中的 id). 区别 在 Swift 中编译器会对 AnyObject 实例的方法调用做检查,还会返回一个 Optional 的结果. 原理 public ty

  • Swift利用Decodable解析JSON的一个小问题详解

    前言 Swift 4是苹果计划于2017年秋季推出的最新版本,其主要重点是提供与Swift 3代码的源兼容性,并努力实现ABI稳定性.从Swift4开始提供的Decodable解析JSON确实很方便,但遇到一个小问题,记录一下. 当JSON中某个key的值为{}或者空字符串"",而该值需要解析的不是基本类型时,即使标记为 Optional,依然会导致整个解析失败: //: Playground import Foundation //Book.swift struct Book: Co

随机推荐