模式匹配中两个最关键的关键词:case 与 where,其中 where 语句可以用来设置约束条件、限制类型等。
在枚举中的应用
- 基本用法。原始值枚举成员的遍历
enum Direction {
case North
case South
case East
case West
}
extension Direction: CustomStringConvertible {
var description: String {
switch self {
case .North: return "↑"
case .South: return "↓"
case .East: return "→"
case .West: return "←"
}
}
}
- 1 由于一个 case 可以匹配多个条件,可以根据实际情况做出修改
extension Direction: CustomStringConvertible {
var description: String {
switch self {
case .North, .South:
return "vertical"
case .West, .East:
return "horizontal"
}
}
}
- 再进一步。关联值枚举对象的匹配,若匹配成功则关联参数值会绑定到新创建的常量或变量上
enum Media {
case Book(title: String, author: String, year: Int)
case Movie(title: String, director: String, year: Int)
case WebSite(url: URL, title: String)
}
extension Media {
var mediaTitle: String {
switch self {
case .Book(title: let aTitle, author: _, year: _):
return aTitle
case .Movie(title: let aTitle, director: _, year: _):
return aTitle
case .WebSite(url: _, title: let aTitle):
return aTitle
}
}
}
- 1 以下两行代码等价
case .Book(title: let aTitle, author: _, year: _):
case let .Book(title: aTitle, author: _, year: _):
- 2 关联参数值匹配固定值
extension Media {
var isFromJulesVerne: Bool {
switch self {
case .Book(title: _, author: "Jules Verne", year: _): return true
case .Movie(title: _, director: "Jules Verne", year: _): return true
default: return false
}
}
}
extension Media {
func checkAuthor(author: String) -> Bool {
switch self {
case .Book(title: _, author: author, year: _): return true
case .Movie(title: _, director: author, year: _): return true
default: return false
}
}
}
- 使用元组包含所有的关联参数
extension Media {
var mediaTitle: String {
switch self {
case let .Book(tuple): return tuple.title
case let .Movie(tuple): return tuple.title
case let .WebSite(tuple): return tuple.title
}
}
}
- 1 同样可以不用指定特定的元组来匹配任何关联值,所以下面四个表达式是等价的
case let .WebSite(tuple):
case let .WebSite:
case let .WebSite(_):
case let .WebSite(_, _):
- 添加 where 子句
extension Media {
var publishedAfter1930: Bool {
switch self {
case let .Book(_, _, year) where year > 1930: return true
case let .Movie(_, _, year) where year > 1930: return true
case .WebSite: return true
default: return false
}
}
}
在元组中的应用
在 Swift 当中,switch 并不像 ObjC 一样只能对整型或枚举进行匹配。可以使用 switch 对很多类型进行匹配,比如元组。这意味着我们只要将多个数据组合在一个元组中,就可以一次性匹配多个数据。此外要注意的是,switch 是按 case 模式被指定的顺序来判断求值的,并且它会在匹配到第一个满足的 case 后跳出。
let point = CGPoint(x: 7, y: 0)
switch (point.x, point.y) {
case (0, 0): print("On the origin!")
case (0, _): print("x=0: on Y-axis!")
case (_, 0): print("y=0: on X-axis!")
case (let x, let y) where x == y: print("On y=x")
default: print("Quite a random point here.")
}
在字符中的应用
let letter: Character = "J"
switch letter {
case "A", "E", "I", "O", "U", "Y": print("Vowel")
default: print("Consonant")
}
在区间中的应用
let count = 7
switch count {
case Int.min..<0: print("Negative count, really?")
case 0: print("Nothing")
case 1: print("One")
case 2..<5: print("A few")
case 5..<10: print("Some")
default: print("Many")
}
func charType(car: Character) -> String {
switch car {
case "A", "E", "I", "O", "U", "Y", "a", "e", "i", "o", "u", "y":
return "Vowel"
case "A"..."Z", "a"..."z":
return "Consonant"
default:
return "Other"
}
}
在类型判断中的应用
使用 as 和 is 匹配指定类型
protocol Medium {
var title: String { get }
}
struct Book: Medium {
let title: String
let author: String
let year: Int
}
struct Movie: Medium {
let title: String
let director: String
let year: Int
}
struct WebSite: Medium {
let url: NSURL
let title: String
}
let media: [Medium] = [
Book(title: "20,000 leagues under the sea", author: "Jules Vernes", year: 1870),
Movie(title: "20,000 leagues under the sea", director: "Richard Fleischer", year: 1955)
]
for medium in media {
print(medium.title)
switch medium {
case let b as Book:
print("Book published in \(b.year)")
case let m as Movie:
print("Movie released in \(m.year)")
case is WebSite: // equal to: let _ as WebSite
print("A WebSite with no date")
default:
print("No year info for \(medium)")
}
}
在可选类型中的应用
let anOptional: Int? = 2
switch anOptional {
case 0: print("Zero") // or `case 0?:`
case 1: print("One") // or `case 1?:`
case 2: print("Two") // or `case 2?:`
case nil: print("None")
default: print("Other")
}
简洁高效的匹配模式 *
if case、guard case、for case [let …] [where …] 都是对 switch-case 的简化,与特定 case 匹配。
比如 if case let x = y { … } 严格等同于 switch y { case let x: … }:当你只想与一条 case 匹配时,这种更紧凑的语法尤其有用。有多个 case 时更适合使用 switch。
另外 if 或 guard 后面如果接 where 子句,需将 where 关键字替换成逗号。
enum Media {
case Book(title: String, author: String, year: Int)
case Movie(title: String, director: String, year: Int)
case WebSite(urlString: String)
}
let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016)
if case let Media.Movie(title, _, _) = m {
print("This is a movie named \(title)")
}
enum NetworkResponse {
case Response(URLResponse, NSData)
case Error(NSError)
}
func processRequestResponse(response: NetworkResponse) {
guard case let .Response(urlResp, data) = response,
let httpResp = urlResp as? HTTPURLResponse, 200..<300 ~= httpResp.statusCode else {
print("Invalid response, can't process")
return
}
print("Processing \(data.length) bytes…")
}
enum Media {
case Book(title: String, author: String, year: Int)
case Movie(title: String, director: String, year: Int)
case WebSite(urlString: String)
}
let mediaList: [Media] = [
.Book(title: "Harry Potter and the Philosopher's Stone", author: "J.K. Rowling", year: 1997),
.Movie(title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", year: 2001),
.Book(title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", year: 1999),
.Movie(title: "Harry Potter and the Chamber of Secrets", director: "Chris Columbus", year: 2002),
]
print("Movies only:")
for case let Media.Movie(title, _, year) in mediaList {
print(" - \(title) (\(year))")
}
在循环遍历中的应用 where
let scores = [20, 8, 59, 60, 70, 80]
scores.forEach {
switch $0 {
case let x where x >= 60:
print("及格")
default:
print("不及格")
}
}
for value in 0..<5 where value > 2 {
print(value)
}
for (index, value) in (0..<5).enumerated() where index > 2 && value > 2 {
print(index, value)
}
在异常捕获中的应用 where
enum ExceptionError: Error {
case httpCode(Int)
}
func throwError() throws {
throw ExceptionError.httpCode(500)
}
do {
try throwError()
} catch ExceptionError.httpCode(let httpCode) where httpCode >= 500{
print("server error")
} catch {
print("other error")
}
在扩展中的应用 where
protocol SomeProtocol {}
class ClassA: SomeProtocol {
let param = 100
}
class ClassB {
let param = 200
}
extension SomeProtocol where Self: ClassA {
func showParam() {
print("ClassA showParam: \(self.param)")
}
}
extension SomeProtocol where Self: ClassB {
func showParam() {
print("ClassB showParam: \(self.param)")
}
}
let a = ClassA()
let b = ClassB()
a.showParam()
// b.showParam()
在泛型中的应用 where
protocol SomeProtocol {}
func test<T: SomeProtocol>(value: T) {}
func test<T>(value: T) where T: SomeProtocol {}
在协议中的应用 where
protocol Sequence {
associatedtype Element where Self.Element == Self.Iterator.Element
}