本文所討論的技巧都是為了避免過(guò)深的嵌套,來(lái)源于 bestswifter 的分享
有時(shí)候,if 和 else 的兩個(gè)分支,體量是完全不對(duì)等的,尤其是在函數(shù)的開(kāi)頭,我們經(jīng)常會(huì)對(duì)一些重要的前提條件做校驗(yàn),如果校驗(yàn)不通過(guò)就不會(huì)做任何后續(xù)的操作:
- (NSString *)handleString:(NSString *)str {
if (![str isKindOfClass:[NSString class]]) {
return nil;
}
else {
// 以下省略 800 字
}
}
可見(jiàn)這個(gè)簡(jiǎn)單的判斷就消耗了一個(gè)縮進(jìn),如果后續(xù)再有多個(gè)判斷,代碼層級(jí)就變得非常惡心,所以正確的做法如下:
- (NSString *)handleString:(NSString *)str {
if (![str isKindOfClass:[NSString class]]) {
return nil;
}
if(str.length <= 0) {
return nil;
}
// ........ 省略多個(gè)判斷
// 這里開(kāi)始寫(xiě)處理邏輯
}
這種做法在 Swift 面有更直接的語(yǔ)義,即 guard 關(guān)鍵字,相信大部分讀者都知道了。
我們思考一下這里的本質(zhì),它其實(shí)是利用了 return 關(guān)鍵字提前終止了代碼的執(zhí)行,然而 return 是作用在整個(gè)函數(shù)上的,只不過(guò)可以起到阻止后續(xù)代碼的執(zhí)行的作用,而且恰好在這里沒(méi)有副作用而已。
只是不是不好理解?沒(méi)關(guān)系,看這個(gè)例子,我改兩句注釋:
- (NSString *)handleString:(NSString *)str {
if (![str isKindOfClass:[NSString class]]) {
return nil;
}
if(str.length <= 0) {
return nil;
}
// ........ 省略多個(gè)判斷
// 第一部分邏輯依賴于前面的判斷,只有判斷通過(guò)的時(shí)候才執(zhí)行
// 第二部分邏輯不依賴于前面的判斷,無(wú)論判斷是否通過(guò)都要執(zhí)行
}
現(xiàn)在的函數(shù)有兩部分邏輯,如果只有第一部分邏輯,使用 return 毫無(wú)問(wèn)題,然而第二部分邏輯要求無(wú)論是否通過(guò)前置校驗(yàn)都要執(zhí)行,顯然 return 關(guān)鍵字就不合適了。
不合適的本質(zhì)原因剛剛說(shuō)了,因?yàn)?return 影響的是整個(gè)函數(shù)的執(zhí)行流程,它在第一段 demo 中能工作只是一個(gè)美麗的巧合。
如果能從這個(gè)角度去思考為什么 return 不行,那么解決方案就很簡(jiǎn)單了:既然 return 影響外層的代碼塊邏輯,那就構(gòu)造一個(gè)臨時(shí)的代碼塊就行了:
- (NSString *)handleString:(NSString *)str {
do {
if (![str isKindOfClass:[NSString class]]) {
break;
}
if(str.length <= 0) {
break;
}
// ........ 省略多個(gè)判斷
// 第一部分邏輯依賴于前面的判斷,只有判斷通過(guò)的時(shí)候才執(zhí)行
}while (0);
// 第二部分邏輯不依賴于前面的判斷,無(wú)論判斷是否通過(guò)都要執(zhí)行
}
在這個(gè) while 循環(huán)里,用 break 就可以退出這個(gè)臨時(shí)構(gòu)造的代碼塊,也不會(huì)影響其它邏輯。