Swift里的switch比OC里面強大很多,switch的主要特性就是模式匹配。
下面先舉個非常簡單的例子。
let people = (age: 27, name: "karl")
switch people {
case (_, let name):
print(name)
default:
break
}
輸出 karl
看完上面的代碼,如果看不懂沒關(guān)系,下面將對模式匹配經(jīng)行詳解。
普通模式
這是最常用的寫法,用法與OC基本相同。
switch 1 {
case 1:
print("1")
}
綁定變量
enum School {
case Student(name: String, age: Int, address: String?)
case Teacher(name: String, age: Int, address: String?)
}
let karl = School.Student(name: "karl", age: 27, address: "AH")
switch student {
case .Student(let name, let age, let address):
print("\(name) -- \(age) -- \(address)")
default:
break
}
我們創(chuàng)建karl這個常量時給其設(shè)置了name,age,address,我們就可以在switch里面通過值綁定的形式將其取出來。
在上面的case .Student(let name, let age, let address):用了三個let顯得有些繁瑣,可以這樣寫case let .Student(name, age, address):
通配符
使用通配符表示忽略這個值,這個值不重要,我不需要使用到它,但是這個也分為兩種情況。
let student = School.Student(name: "karl", age: 27, address: nil)
let teacher = School.Teacher(name: "wang", age: 50, address: "AH")
- _ :這個值完全不重要,可有可無,是什么我都不關(guān)心。
switch student {
case let .Teacher(name, _, _):
print("name: \(name)")
default: break
}
這里第二個參數(shù)和第三個參數(shù)就是使用了通配符表示,不關(guān)心它是什么值,只要前面參數(shù)能匹配成功就可以,就算成立。
- _? :這個值我雖然不用,但是它必須有值。
switch student {
case let .Student(name, age, _?):
print("name: \(name)")
default: break
}
這里第三個參數(shù)我使用了_?,表示這個值我不用,但是不能為nil
使用于元祖
就是我最上面舉的那個例子。
let people = (age: 27, name: "karl")
switch people {
case (_, let name):
print(name)
default:
break
}
我并不關(guān)心age這個值,我只想取出name。
在條件中使用where語句
switch student {
case let .Student(_, age, _) where age > 30:
print("\(age)")
default:
break
}
當(dāng)case匹配成功,并且綁定的值要符合where的條件才算真的成立。
匹配Range
let number = 10
switch number {
case 0 ... 7:
print("0 ... 7")
case 10 ..< 20:
print("10 ..< 20")
default:
break
}
輸出: 10 ..< 20
可以通過區(qū)間的形式來匹配
類型推斷和類型轉(zhuǎn)換
protocol Animal { }
struct Dog: Animal { }
struct Cat: Animal { }
var animalArray = [Animal]()
let dog = Dog()
let cat = Cat()
animalArray.append(dog)
animalArray.append(cat)
for item in animalArray {
switch item {
case is Dog:
print("dog")
case let cat as Cat:
print("cat")
default:
break
}
}
可以通過is判斷這個值是否是某個類型。是的話,表示匹配成功。
通過 as可以將這個值嘗試轉(zhuǎn)換為某個類型,如果成功,則表示匹配成功。
模式匹配操作符
struct Size {
var width: CGFloat
var height: CGFloat
}
let size = Size(width: 20, height: 20)
func ~=(lhs: CGFloat, rhs: Size) -> Bool {
return lhs == rhs.height * rhs.width
}
switch size {
case 400:
print("??")
default:
print("!!!")
}
我們可以重載~=這個操作符,讓我們自定義switch匹配規(guī)則
看上面的代碼,我們重載了~=操作符,操作符有兩個參數(shù),返回一個Bool值。
- 參數(shù)1:switch中case后的值
- 參數(shù)2:switch中傳入的值
- 返回值:是否匹配成功
我的這個~=方法定義的是size的width * height 等于 case的值就表示匹配成功。
這里只是一個簡單的實驗。具體用途需要看實際情況。
關(guān)于Optionals
let count: Int? = 5
switch count {
case 1?:
print("1")
case 3?:
print("3")
case 5?:
print("5")
case _:
print("nil")
default:
break
}
匹配一個可選值,要使用?的形式,要不然會報類型不匹配的錯。
這個時候count其實相當(dāng)于Optional.some(5)
而下面的各個case相當(dāng)于
case Optional.some(1):
print("1")
case Optional.some(3):
print("3")
case Optional.some(5):
print("5")
使用?的形式寫,算是一個語法糖
if case let
if case let a = b {
}
表示b是否能匹配上a。
這就相當(dāng)于
switch b {
case let a:
break
}
當(dāng)只有一個條件的時候,用switch會顯得冗余,直接用if case let會使代碼讀起來更便捷。
直接拿上面的綁定變量那一節(jié)的內(nèi)容來說,如果直接這么寫的話,會顯得更易讀,而且也會少點代碼。
if case let .Student(name, age, _?) = student {
print(name)
}
當(dāng)然,這里也是可以用where語句的,但是在swift3以后,有些場景的where被逗號給代替了,如果給上面的if case let加上where條件的話,就是這樣:
if case let .Student(name, age, _?) = student, age > 20 {
print(name)
}
一行代碼就可以寫出一個很強的表達式。
guard case let
guard case let的用法和if case let是一樣的,區(qū)別也僅僅是if和guard的用法區(qū)別,就不多啰嗦。
for case let
一個數(shù)組里存放的是Any類型的可選值,我想輸出這個數(shù)組里面大于10的數(shù)字。
let array: [Any?] = [1,2,nil,4,54,65,76, ""]
在不知道 for case let之前,寫法是這樣的。
先遍歷出所有的元素,在嘗試元素轉(zhuǎn)成Int,在判斷是否大于10。
for item in array {
if let element = item as? Int, element > 10 {
}
}
在看看下面這種寫法。
先將遍歷出來的元素綁定到x上,并且x不能為nil,然后在用where增加一個要是>10的條件。
是不是感覺清爽很多,又是一行代碼寫出一個強有力的表達式。
for case let x? as Int? in array where x > 10 {
print(x)
}
在舉個例子,還用一下之前定義的enum School
let xiaoming = School.Student(name: "xiaoming", age: 10, address: nil)
let wang = School.Teacher(name: "wang", age: 50, address: "AH")
let li = School.Teacher(name: "li", age: 50, address: "ZJ")
let xiaoqiang = School.Student(name: "xiaoming", age: 14, address: "BJ")
let xiaohao = School.Student(name: "xiaohao", age: 17, address: "SH")
這里有5個School類型的常量,有2個Teacher,3個Student。
把他們都加到一個數(shù)組里,
let schoolArray = [xiaoming, wang, xiaoqiang, li, xiaohao]
我現(xiàn)在想獲取到數(shù)組里的Student,并且address不能為空,并且age要小于15的。
看看我們使用for case let怎么解決。
for case let .Student(name, age, _?) in schoolArray where age < 15 {
print(name)
}
還是一行代碼,帥!
很多時候,我們在網(wǎng)上看到一些新的語法,看著介紹很牛逼,也都能看明白,但是一讓自己放到項目里就懵逼,因為沒用過,所有壓根不知道這種形式改怎么用到項目里。
這個舉個最常見的的簡單的例子,網(wǎng)絡(luò)請求。
enum Result {
case success(Data)
case error(Error)
}
func loadNetworkData(_ urlString: String, completion: @escaping (Result) -> ()) {
guard let url = URL(string: urlString) else {
fatalError("url 錯誤")
}
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
var result: Result!
if let e = error {
result = Result.error(e)
completion(result)
return
}
if let d = data {
result = Result.success(d)
completion(result)
}
}
task.resume()
}
loadNetworkData("") { (result) in
switch result {
case let .success(data):
print("scrress: \(data)")
case let .error(error):
print("error: \(error)")
}
}
定義了一個enum Result,里面有兩個case,分別表示網(wǎng)絡(luò)請求成功和失敗,成功則給一個Data,失敗則給一個Error。
然后有一個加載網(wǎng)絡(luò)數(shù)組的方法func loadNetworkData,方法就是最普通的網(wǎng)絡(luò)請求,在請求回調(diào)里,判斷error是否有值,如果有值則創(chuàng)建一個error類型的Result,如果data有值,則創(chuàng)建一個success類型的Result。
在發(fā)起網(wǎng)絡(luò)請求的方法回調(diào)里,就可以通過模式匹配的方法判斷回調(diào)回來的Result到底是什么類型,然后通過值綁定的形式,取出success的data或者是error的值。
當(dāng)然這只是一個簡單的寫法,只是提供一點思路。具體怎么在項目里面用還是需要去實踐。
這就是個人總結(jié)的一些模式匹配的用法,都是自己在工作過程和學(xué)習(xí)過程中總結(jié)的一些經(jīng)驗,有自己的使用總結(jié),當(dāng)然也有在網(wǎng)上看一些博客的總結(jié),希望對能讀到這篇文章的同學(xué)有所幫助。
如果文章中有什么使用不當(dāng)和使用錯誤的,一定請指出來。謝謝!