阅读 164

Swift 中的 Switch 语句之常见模式匹配用法

点赞评论,希望能在你的帮助下越来越好

作者:@iOS成长指北,本文首发于公众号 iOS成长指北,欢迎各位前往指正

如有转载需求请联系我,记住一定要联系哦!!!

这是我参与更文挑战的第3天,活动详情查看:更文挑战

笔者在 Day13 - Swift 基础总结,1 中简述了 Swift 中 Switch 的用法。随着学习的过程,今天我们探讨一下在 Swift 中的模式匹配的用法,主要着力于 Switch 中的实现。

通配符模式

通配符模式由一个下划线(_)构成,用于匹配并忽略任何值,当你想忽略被匹配的值时可以使用该模式。

当我们处理具有关联值的枚举或者是元祖时,当我们想忽略某些匹配值时,使用这种模式

//枚举
enum Response {
    case error(Int, String)
    case success
}
let httpResponse = Response.error(300, "Network Error")
switch httpResponse {
case let .error(_,  status):
    print("HTTP Error: \(status)")
case .success:
    print("HTTP Request was successful")
}
//元祖
switch (15, "example", 3.14) {
  case (_, _, let pi):
  print ("pi: \(pi)")
}

复制代码

一个不常见的示例是利用这种模式来进行可选项解包,可以通过附加 ? 来匹配可选项,使其成为_?

let p: String? = ""
switch p {
case _?:
    print("p = ", p as Any)
case nil:
    print("no words")
}
复制代码

更多关于 Swift 中的可选,可以查看笔者之前的 Day12 - 可选

标识符模式

标识符模式可以匹配任何值,并将匹配的一个变量或者常量绑定起来。

对于确定值的匹配,使用 Switch 而不是 if...else 代码更加的易读:

let name = "Harry"
switch name {
case "Harry":
    print("哈利")
case "iOS":
    print("成长指北")
default:
    print("哈利波特")
}
复制代码

值绑定模式

值绑定模式把匹配到的值绑定给一个变量或者常量。

我们可以利用值绑定模式与其他模式进行匹配,例如,当我们实现一个实现用户登录功能的方法时,我们可以这么做

func usernameAndPassword() 
    -> (username: String, password: String)? {
      //获取账户/密码,这里处理一些关于账户密码的校验
}
func usernameAndPassword()
    -> (username: String, password: String)? {
      //获取账户/密码,这里处理一些关于账户密码的校验
    return (username: "iOS成长指北", password: "123123")
}

switch usernameAndPassword() {
case let (username, _)? where username == "iOS成长指北":
    attention()
default:
    like()
}

func attention() {
    //关注
}

func like() {
    //点赞
}

复制代码

你可以为特定的登录账户或密码做特定的事情。

元组模式

元组模式是由都好分隔的,具有零个或多个模式的列表,并由一对圆括号括起来。元组模式匹配到响应的元组类型值。

对多个条件的数据,我们可以利用元祖来匹配我们需要的条件:

let age = 23
let job: String? = "Operator"
let payload: Any = NSDictionary()

switch (age, job, payload) {
case (let age, _?, _ as NSDictionary):
    print(age)
default: ()
}
复制代码

题外话:

最新新学到的一个关于利用元祖交换两个数据的方法

func swapTwoInTuple(_ x: inout Int, _ y: inout Int ) {
 (y, x) = (x, y)
}

var x = 10, y = 20

swapTwoInTuple(&x, &y)
复制代码

枚举用例模式

枚举用例模式匹配现有的某个枚举类型的某个用例。这应该是 Switch 中最常见的匹配模式了。对于 Switch 中关于枚举用例的匹配情况,这里主要关注的是关于枚举用例的关联值,同元组模式一样,我们可以可以对关联值进行多种条件判断。

enum Response {
    case error(Int, String)
    case success
}
let httpResponse = Response.error(300, "Network Error")
switch httpResponse {
case let .error(errorCode, status) where errorCode >= 300 && errorCode < 399 :
    print("HTTP Error: \(status)")
case let .error(errorCode, status) where errorCode >= 400 && errorCode < 499 :
    print("HTTP Error: \(status)")
case let .error(errorCode, status) where errorCode >= 500 && errorCode < 599 :
    print("HTTP Error: \(status)")
case .success:
    print("HTTP Request was successful")
default:()
}
复制代码

类型转换模式

有两种类型转换模式, is 模式as 模式is 模式只出现在 Switch 语句中用例(case)中。

is 模式as 模式

  • is 模式仅当一个值的类型在运行时和 is 模式右边的指定类型一致,或者是其子类的情况下,才会匹配这个值。is 模式is 运算符有相似表现,它们都可进行类型转换,但是 is 模式忽略返回的类型。所以 is 模式 无法使用值绑定

  • as 模式仅当一个值得类型在运行时和 as 模式右边指定类型一致或者是其子类的情况下,才会匹配这个值,如果匹配成功,被匹配的值得类型被转换成 as 模式右边指定的类型返回

下面这个简单的例子说明了其中的区别

let num: Any = 5
switch num {
case is Int:
  print (a + 1)
case let n as Double:
  print (n + 1)
default: ()
}
复制代码

表达式模式

表达模式非常强大。它将 Switch 的值与实现 〜= 运算符的表达式进行匹配。此运算符有默认实现,例如区间、数组,在之前的学习中我们介绍了关于区间的匹配:

let wavelength = 620

switch wavelength {
case 380..<450:
    print("Purple!")
case 450..<495:
    print("Blue!")
default:
    print("Not in visible spectrum")
}

复制代码

关于数组的匹配也是一个很好的例子

let myArray = [1, 2, 4, 3]
switch myArray {
case [..., 0, 0, 0]:
  //以 3 个 0 结尾
case [4, ...]:
  //以 4 开头
case [_, 2, _, 3]:
  //第二个第四个是 2,3
case [_, _, 8]:
  //第三个是 8
default: ()
}
复制代码

出来本身支持 〜= 运算符的类来说,对于自定义类来说,我们可以通过自定义 〜= 运算符来进行响应的匹配。实现 ~= 的函数原型如下,其中 PatternTypePredicateType 可以替换为你想要的任何类型:

func ~=(pattern: PatternType, predicate: PredicateType) -> Bool
复制代码

我们尝试一下下面的例子

struct Person {
    var money: Int
}

func ~=(pattern: Range<Int>, predicate: Person) -> Bool {
    return pattern.contains(predicate.money)
}
var reader: Person = Person(money: 1000)

switch reader {
case 0..<1000:
    print("我")
case 1000..<5000:
    print("点赞的")
case 5000..<10000:
    print("分享的")
default:
    print("关注的")
}
复制代码

更多关于运算符重载可以看看笔者之前写的 深入理解 Swift Operators

switch or if ?

在控制流中或者是进行条件判断时。我们应该如何选择,是使用 switch 还是使用 if

  • 对于枚举类型的匹配,自然毋庸置疑的是使用 switch 了。因为 Swift 要求它的 switch 语句是详尽的。在不适用 default 的情况下,用 switch 可以使 代码更具完整性和确保不会遗漏。

    一般如果只有两种类型的话,除非是有其他操作逻辑的话,用 if 会比使用 switch 更好

  • 当您使用 switch 检查一个值是否有多个可能的结果时,该值将仅读取一次,而使用 if 进行判断是则会被读取多次。当使用函数调用时,这变得尤为重要,因为其中一些调用速度可能很慢。

    虽然进行函数调用时,我们一般都是先获取到值,在进行语句判断

  • switch 支持 元组、区间、值绑定还有 where ,并且支持模式匹配,在处理复杂条件时一般建议你使用 switch


如果你有任何问题,请直接评论,如果文章有任何不对的地方,请随意表达。如果你愿意,可以通过分享这篇文章来让更多的人发现它。

感谢你阅读本文! 🚀

文章分类
iOS
文章标签