在上一篇文章Swift中一些常見(jiàn)的關(guān)鍵字一(inout,defer,throw等)簡(jiǎn)單的介紹了幾個(gè)關(guān)鍵字。本文續(xù)寫(xiě)。
guard
guard關(guān)鍵字和常見(jiàn)的if關(guān)鍵字是一對(duì)兄弟般的關(guān)系。兩者的目的是同樣的 -- 進(jìn)行條件判別;只是在使用上有所不同。
我們很熟悉if-else,它表示條件成立則執(zhí)行if中的內(nèi)容,不然執(zhí)行else語(yǔ)句中的內(nèi)容。而guard-else則表示,如果guard成立則繼續(xù)往下執(zhí)行,不然在else中return。
對(duì)比一段代碼加深了解:
// if - else
let a = 100
if a % 20 == 0{
if a % 5 == 0{
print(a / 5)
}
}
return
/// 這是一個(gè)雙重判斷語(yǔ)句,需要在兩個(gè)`if`條件都通過(guò)的情況下,才能執(zhí)行到我們真正的邏輯代碼。
很顯然,這種嵌套寫(xiě)法很不友好,可讀性也很差,可以進(jìn)行改進(jìn):
let a = 100
if a % 20 == 0 && a % 5 == 0{
print(a / 5)
}
return
盡管這樣這樣已經(jīng)很簡(jiǎn)潔了,但是當(dāng)我們的條件越來(lái)越多的時(shí)候, if 判斷的代碼將變的更長(zhǎng)更復(fù)雜,這種方式的可讀性其實(shí)也不好。不僅如此,多條件的綜合判斷將使得代碼更加混亂,可讀性也隨之降低,這并不十分可取。
那繼續(xù)改進(jìn)一下:
let a = 100
if a % 20 != 0 { return }
if a % 5 != 0{ return }
print(a / 5)
這里我們思維轉(zhuǎn)換了一下,取他們的相反的邏輯進(jìn)行處理。理解起來(lái)的話,不再是如果......就......,而是除非......那么。
顯然這種方式的可讀性是最好的,我個(gè)人推薦這種方式。 但是,這種方式并非完美,因?yàn)槭褂?code>if實(shí)現(xiàn)除非語(yǔ)法,我們總要謹(jǐn)記在括號(hào)內(nèi)帶上return字段,如果漏掉,代碼邏輯就會(huì)出現(xiàn)問(wèn)題,而編譯器卻不會(huì)報(bào)錯(cuò) --- 這增加了代碼bug的概率。Swift提供了一種安全的方式,書(shū)寫(xiě)除非......語(yǔ)句 --- guard-else。
同樣,上面的代碼使用guard-else來(lái)實(shí)現(xiàn)如下:
let a = 100
guard a % 20 == 0 else{ return }
guard a % 5 == 0 else{ return }
print(a / 5)
Swift規(guī)定,guard - else必須同時(shí)出現(xiàn),而且,else中,必須進(jìn)行return。
這就保證了我們不會(huì)漏掉return,同時(shí),guard條件本身也是正相關(guān):如果條件成立則執(zhí)行下一步。這符合我們的思維習(xí)慣,相比之下, if ! 則需要我們對(duì)條件進(jìn)行一個(gè)非轉(zhuǎn)換,和我們的思維習(xí)慣不合。這里體現(xiàn)了guard - else的優(yōu)勢(shì)。
當(dāng)然,guard - else不能取代if - else。如果是兩個(gè)選擇判斷語(yǔ)句中,我們還是使用if -- else語(yǔ)法更為合理。比如:
//這種不存在結(jié)束的條件判斷,并不適合使用guard關(guān)鍵字。
let a = 100
if a > 80{
print(a-80)
}esle{
print(80-a)
}
總結(jié),
guard - else在除非 - 則.. - 不然 - 結(jié)束的語(yǔ)法中,使用最為合適。
if let
if let 關(guān)鍵字是一個(gè)組合關(guān)鍵字。我們主要使用它解決Optional對(duì)象解包時(shí)產(chǎn)生空對(duì)象的處理。
Swift中,有著嚴(yán)格類型安全要求,如果一個(gè)對(duì)象可能為nil的時(shí)候,我們需要對(duì)這個(gè)對(duì)象用?進(jìn)行修飾,就像下面這個(gè)屬性:
var prop: String? = nil //初始化為一個(gè)空對(duì)象
如果我們需要使用這個(gè)prop的時(shí)候,由于這是一個(gè)封包(Optional)對(duì)象,也叫可選對(duì)象,如果直接調(diào)用,需要帶上!修飾符號(hào),就像這樣:
self.prop! = "test"
prop后的!會(huì)被強(qiáng)制攜帶,不然會(huì)報(bào)錯(cuò)。這是因?yàn)?,賦值操作是一個(gè)確定類型的動(dòng)作,所以需要使用 ! 對(duì) ?修飾的可選對(duì)象進(jìn)行解包。這種寫(xiě)法其實(shí)欠妥的,因?yàn)椋?dāng)prop是一個(gè)nil的時(shí)候,上面這個(gè)語(yǔ)句將會(huì)報(bào)錯(cuò),原因:為nil賦值是不允許的。
當(dāng)然,我們可以對(duì)這個(gè)對(duì)象進(jìn)行判斷,如果不為空才執(zhí)行賦值操作。如:
if self.prop != nil{ self.prop! = "test" }
或者
guard self.prop != nil else{ return }
self.prop! = "test"
而實(shí)際上,在Swift中,提供一種專門(mén)的方式用于解包時(shí)的操作,那就是if let 關(guān)鍵字。依然是上面的例子,使用if let 書(shū)寫(xiě)的話如下:
if let aProp = self.prop{
aProp = "test"
...其他更多的操作
}
這個(gè)例子是對(duì)顯式的封包屬性進(jìn)行操作,除此之外,if let 還可以用于強(qiáng)制的類型轉(zhuǎn)換。
正常情況下,我們?cè)赟wift 中是不能進(jìn)行類型轉(zhuǎn)換的,如果強(qiáng)轉(zhuǎn),導(dǎo)致Crash的概率很高 ---- 就算是開(kāi)發(fā)者,也同樣不能保證自己的對(duì)象類型是不是真的是要轉(zhuǎn)化的類型。if let 用于可像下面這樣安全的進(jìn)行強(qiáng)制轉(zhuǎn)換:
if let typeAObj = typeBObj as? typeA{
// 如果能夠轉(zhuǎn)換成功,那么typeAObj就是強(qiáng)轉(zhuǎn)后的對(duì)象
....
}else {
// 轉(zhuǎn)化失敗,這里一般不會(huì)做處理
}
if case
在學(xué)習(xí) if case之前,我們先想想,那些地方使用到了case 這個(gè)關(guān)鍵字,毫無(wú)疑問(wè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)然可以通過(guò)添加屬性值的方式更簡(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的品牌而已,但是我卻需要寫(xiě)一部分跟這個(gè)品牌無(wú)關(guān)的代碼。我們可以進(jìn)行一點(diǎn)簡(jiǎn)化:
switch myBrand{
case .BMW: do{ print("寶馬") }
default: () // do nothing
}
可能覺(jué)得這個(gè)方式還是過(guò)于繁瑣,有什么辦法可以像if那樣對(duì)枚舉值判斷嗎?
if case應(yīng)該能幫到你:
let myBrand = CarBrand.BMW
if case .BMW = myBrand{
print("寶馬")
}
這樣不就舒服多了,不僅沒(méi)有冗余的代碼,還有具備了更好的可讀性。
if case let
if case let它實(shí)際上不是一個(gè)完整的關(guān)鍵字,它 if case和let的組合。我們同樣會(huì)在對(duì)枚舉類型進(jìn)行模式匹配的時(shí)候用到它,不過(guò)這個(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í)可以的。同樣我們覺(jué)得這樣還是過(guò)于繁瑣了。
使用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從句寫(xiě)出更加優(yōu)雅的代碼哦。