if case
在學(xué)習(xí) if case之前,我們先想想,那些地方使用到了case 這個(gè)關(guān)鍵字,毫無疑問,絕大多數(shù)使用case的時(shí)候,是在 switch語(yǔ)法中。
Swift中的if case主要用于模式匹配。跟switch很類似,但它本身有特別的使用場(chǎng)景,區(qū)別switch在于,switch用于對(duì)所有的對(duì)象可能值進(jìn)行判斷,而if case之需要對(duì)關(guān)注的可能值進(jìn)行判斷。老規(guī)矩,還是代碼上看看。
假設(shè)我有一個(gè)枚舉變量CarBrand,汽車品牌。它的結(jié)構(gòu)如下:
enum CarBrand{
case BMW
case AUDI
case BENZ
}
如果使用switch進(jìn)變量匹配,并按照不同的車的品牌輸出對(duì)應(yīng)的中文名字(這種需求當(dāng)然可以通過添加屬性值的方式更簡(jiǎn)便的解決,但這里我們只討論if case的使用,暫時(shí)不關(guān)注其他的方式。)。我們可能是這樣做的:
let myBrand = CarBrand.BMW
switch myBrand{
case .BMW: do{ print("寶馬") }
case .AUDI: do{ print("奧迪") }
case .BENZ: do{ print("奔馳") }
}
實(shí)際上,我們可能僅僅是想找出BMW的品牌而已,但是我卻需要寫一部分跟這個(gè)品牌無關(guān)的代碼。我們可以進(jìn)行一點(diǎn)簡(jiǎn)化:
switch myBrand{
case .BMW: do{ print("寶馬") }
default: () // do nothing
}
可能覺得這個(gè)方式還是過于繁瑣,有什么辦法可以像if那樣對(duì)枚舉值判斷嗎?
if case應(yīng)該能幫到你:
let myBrand = CarBrand.BMW
if case .BMW = myBrand{
print("寶馬")
}
這樣不就舒服多了,不僅沒有冗余的代碼,還有具備了更好的可讀性。
if case let
if case let它實(shí)際上不是一個(gè)完整的關(guān)鍵字,它if case和let的組合。我們同樣會(huì)在對(duì)枚舉類型進(jìn)行模式匹配的時(shí)候用到它,不過這個(gè)情況稍微復(fù)雜一點(diǎn)。
假設(shè)我的CarBrand枚舉想要集合更多的東西,比如它的中文名字和生產(chǎn)地。我們可以這樣定義這個(gè)枚舉:
enum CarBrand{
case BMW(name:String,Production:String)
case AUDI(name:String,Production:String)
case BENZ(name:String,Production:String)
}
現(xiàn)在我定一個(gè)枚舉變量:
let myCar = CarBrand.BMW(name: "寶馬",Production: "德國(guó)")
如果我想輸出myCar的關(guān)聯(lián)屬性,直接使用點(diǎn)語(yǔ)法什么的都顯然是不行的。我們可在枚舉中自定義一些輸出關(guān)聯(lián)屬性的方法,但是這個(gè)做法比較繁瑣,并且破壞了枚舉結(jié)構(gòu)。
比較簡(jiǎn)單的是可以使用switch:
switch myCar {
case let CarBrand.BMW(name,Production):
print("This car named \(name),from\(Production)")
default: () // 不做任何處理
}
這樣確實(shí)可以的。同樣我們覺得這樣還是過于繁瑣了。
使用if case let:
let myBrand = CarBrand.BMW(name: "寶馬",Production: "德國(guó)")
if case let CarBrand.BMW(name, Production) = myBrand{
print("\(name),\(Production)")
}
是不是更加簡(jiǎn)潔呢。當(dāng)然,if case let 還可一配合where從句寫出更加優(yōu)雅的代碼哦。
關(guān)于大于或者小于的匹配
//傳統(tǒng)用法
if x>=6 && x < 12 {
}
//模式匹配用法
if case 6..<12 = x {
}
復(fù)雜一點(diǎn),如果x是Optional對(duì)象,這里可以采用嵌套的模式匹配
//傳統(tǒng)用法
if let x = x, x>=6 && x < 12 {
}
//模式匹配用法
if case .some(6..<12) = x {
}
pattern有很多種,看一下官方文檔
GRAMMAR OF A PATTERN
pattern → wildcard-pattern
pattern → identifier-pattern
pattern → value-binding-pattern
pattern → tuple-pattern
pattern → enum-case-pattern
pattern → optional-pattern
pattern → type-casting-pattern
pattern → expression-pattern
這里不詳細(xì)介紹這些pattern,更多的應(yīng)該switch里面去學(xué)習(xí),也就是說switch里面可以使用的在if里面都可以使用
這里舉一些例子
1. type-casting-pattern
var t : Any = 10
if case is Int = t {
print("bingo")
}
if t is Int {
}
上面兩種用法結(jié)果是一樣的,一種是模式匹配,另外一種是常用的is operator
2. tuple-pattern
if case (1..<10, 1..<20) = (7, 8) {
}
在這里(1..<10, 1..<20)是一個(gè)pattern,而不是普通的tuple,這里會(huì)對(duì)左右兩邊的tuple的元素一一進(jìn)行patteren match
如果把前面換成一個(gè)tuple就會(huì)出錯(cuò)里,試試看下面的代碼
let pattern = (1..<10, 1..<20)
if case pattern = (7,8) {
}
3. optional-pattern
var t : Any? = 10
// 判斷t是不是nil,和判斷 t != nil 等效
if case _? = t {
}
//判斷t是不是nil,如果有值則綁定到x
if case let x? = t {
}
4. expression-pattern
前面提到的case 6..<12 = x實(shí)際上就是這一種pattern,實(shí)際上這里調(diào)用了一個(gè)函數(shù),也是一個(gè)操作符~=
func ~= (pattern: String, value: Int) -> Bool {
return pattern == "\(value)"
}
if case "123" = 123 {
}
通過重載~=操作符,我們可以實(shí)現(xiàn)很多自定義的模式匹配,結(jié)合起來可以有很多有趣的用法
上面通過一些例子介紹了if中的模式匹配用法,在代碼中也有常常遇到這種用法,這種用法可以看成是switch case的一種簡(jiǎn)易寫法,這樣理解起來就比較容易了
if case expression1 = expression2 {
statements
}
//等價(jià)于
switch expression2 {
case expression1:
statements
default:
break
}
available用法
這是一種特殊的if用法,用來判斷運(yùn)行環(huán)境
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}
這個(gè)比較簡(jiǎn)單,不再介紹
混合使用
上面四種if的使用方法可以混合使用,在一個(gè)if語(yǔ)句中可以有多個(gè)condition,通過,分開,這些condition會(huì)依序執(zhí)行
var t : Any? = 10
if case let xs? = t, xs is Int {
print("bingo")
}
這里會(huì)首先將t進(jìn)行optional-pattern的匹配和綁定,然后判斷xs是否是Int
condition之前常用&&操作符進(jìn)行的判斷也可以換成使用逗號(hào)分割,效果是一樣的
最后看一下官方文檔對(duì)if和condition list的描述
可選類型的模式匹配


swift之模式(Pattern)
模式簡(jiǎn)介
模式代表單個(gè)值或者復(fù)合值的結(jié)構(gòu)。比如
(10,20)和("Tom","Mary")在結(jié)構(gòu)上并無本質(zhì)差別,都是包含兩個(gè)值的元組。swift中的模式分為兩類,一類能成功匹配任何類型的值,另一類在運(yùn)行時(shí)匹配某個(gè)特定值時(shí)可能會(huì)失敗。
第一類用于解構(gòu)簡(jiǎn)單變量、常量和可選綁定中的值。包括通配符模式 和標(biāo)識(shí)符模式以及包含這兩種模式的值綁定模式和元組模式??梢詾檫@類模式制定一個(gè)類型標(biāo)注,從而限定他們只匹配某種特定類型的值。
第二類用于全模式匹配。,這種情況下你視圖匹配的值在運(yùn)行時(shí)可能不存在。包括枚舉用例模式、可選模式、表達(dá)式模式和類型轉(zhuǎn)換模式。
具體實(shí)例
- 通配符模式(Wildcard Pattern):不關(guān)注匹配的值
for _ in 1...3 {
// Do something three times
}
標(biāo)識(shí)符模式(Identifier Pattern):匹配任何值并且把匹配到的值綁定給變量或者常量。
值綁定模式(Value-Binding pattern):把匹配到的值綁定給一個(gè)變量或者常量,綁定給常量時(shí)用
let,綁定給變量時(shí)用var。
let point = (1,2)
switch point {
case let (x,y):
print("The point is at (\(x), \(y))") //The point is at (1, 2)
}
- 元組模式
- 逗號(hào)分隔的具有一個(gè)或者多個(gè)模式的列表
- 可以使用類型標(biāo)注去限定一個(gè)元組模式可以匹配那些元組類型。
- 元組模式被用于
for-in語(yǔ)句或者變量火證常量聲明時(shí),金可以包含通配符模式、標(biāo)識(shí)符模式、可選模式或者其他包含這些模式的元組模式。
let points = [(0,0),(1,0),(2,0),(0,3),(1,1),(2,1)]
for (x,y) in points where y == 0 || x == 0 {
print("Point(\(x), \(y)) is on axis!")
}
for point in points {
switch point {
case (_,0), (0,_):
print("Point(\(point.0), \(point.1)) is on axis!")
default:
break
}
}
- 枚舉用例模式(Enumeration Case Pattern)
enum SomuEnum {
case left,right
}
let direction: SomuEnum? = .left
switch direction {
case .left:
print("Turn left!")
case .right:
print("Turn right!")
case nil:
print("Keep going straiht!")
}
var someOptional: Optional<Int> = 43
//枚舉用例模式匹配
switch someOptional {
case .some(let x):
print(x) // 43
case .none:
print("nil")
}
if case .some(let x) = someOptional {
print(x) // 43
}
可選項(xiàng)模式(Optional Pattern)
可選項(xiàng)模式匹配
Optional<Wrapped>枚舉在some(Wrapped)中包裝的值。
var someOptional: Optional<Int> = 43
if case let x? = someOptional {
print(x) // 43
}
let arrayOfOptionalInts: [Int?] = [nil,2,3,5,nil]
for case let number? in arrayOfOptionalInts {
print("Found a \(number) !")
}
類型轉(zhuǎn)換模式(Type-Casting Pattern)
is模式:只出現(xiàn)在switch語(yǔ)句的case標(biāo)簽中,當(dāng)一個(gè)值的類型在運(yùn)行時(shí)與模式右邊指定類型一致或者是其子類的情況下才會(huì)匹配。as模式:當(dāng)一個(gè)值的類型在運(yùn)行時(shí)和右邊指定類型一致或者是其子類的情況下,才會(huì)匹配。如果匹配成功,被匹配值的類型被撞換成as模式右邊指定的類型。
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init( name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library: [MediaItem] = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
for mediaItem in library {
switch mediaItem {
case is Song :
print("Song: \(mediaItem.name)")
case let movie as Movie:
print("Movie: \(movie.name) director: \(movie.director)")
default:
break
}
}
-
表達(dá)式模式(Expression Pattern)
只出現(xiàn)在
switch的case標(biāo)簽中。表達(dá)式模式代表的表達(dá)式會(huì)使用Swift的標(biāo)準(zhǔn)庫(kù)中的
~=運(yùn)算符與輸入表達(dá)式的值進(jìn)行比較,如果~=預(yù)算內(nèi)算符返回true,則匹配成功。因此自定義類型要使用表達(dá)式模式匹配,需要重載~=運(yùn)算符。
let point = (0,0)
func ~= (pattern: String, value: Int) -> Bool {
return pattern == "\(value)"
}
switch point {
case ("0","0"):
print("(0,0) is at the origin.")
default:
print("The point is at (\(point.0), \(point.1))")
}