條件語句

2018-08-12 21:19 更新

條件語句

條件語句體應(yīng)該總是被大括號(hào)包圍來避免錯(cuò)誤,即使可以不用(比如,只有一行內(nèi)容)。這些錯(cuò)誤包括多加了第二行,并且誤以為它是 if 語句體里面的。此外,更危險(xiǎn)的可能是,如果把 if 語句體里的一行注釋掉了,之后的一行代碼會(huì)不知不覺成為 if 語句里的代碼。

推薦:

if (!error) {
    return success;
}

不推薦:

if (!error)
    return success;

或者

if (!error) return success;

在 2014年2月 蘋果的 SSL/TLS 實(shí)現(xiàn)里面發(fā)現(xiàn)了知名的 goto fail 錯(cuò)誤。

代碼在這里:

static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
                                 uint8_t *signature, UInt16 signatureLen)
{
  OSStatus        err;
  ...

  if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
  if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;
  ...

fail:
  SSLFreeBuffer(&signedHashes);
  SSLFreeBuffer(&hashCtx);
  return err;
}

顯而易見,這里有沒有括號(hào)包圍的2行連續(xù)的 goto fail; 。我們當(dāng)然不希望寫出上面的代碼導(dǎo)致出現(xiàn)錯(cuò)誤。

此外,在其他條件語句里面也應(yīng)該按照這種風(fēng)格統(tǒng)一,這樣更便于檢查。

尤達(dá)表達(dá)式

不要使用尤達(dá)表達(dá)式。尤達(dá)表達(dá)式是指,拿一個(gè)常量去和變量比較而不是拿變量去和常量比較。它就像是在表達(dá) “藍(lán)色是不是天空的顏色” 或者 “高個(gè)是不是這個(gè)男人的屬性” 而不是 “天空是不是藍(lán)的” 或者 “這個(gè)男人是不是高個(gè)子的”

Yoda

(譯者注:名字起源于星球大戰(zhàn)中尤達(dá)大師的講話方式,總是用倒裝的語序)

推薦:

if ([myValue isEqual:@42]) { ...

不推薦:

if ([@42 isEqual:myValue]) { ...

nil 和 BOOL 檢查

On a similar note of the Yoda conditions, also the nil check has been at the centre of debates. Some notous libraries out there use to check for an object to be or not to be nil as so:

【疑問】 類似于 Yoda 表達(dá)式,nil 檢查的方式也是存在爭(zhēng)議的。一些 notous 庫(kù) 像這樣檢查對(duì)象是否為 nil:

if (nil == myValue) { ...

或許有人會(huì)提出這是錯(cuò)的,因?yàn)樵?nil 作為一個(gè)常量的情況下,這樣做就像 Yoda 表達(dá)式了。 但是一些程序員這么做的原因是為了避免調(diào)試的困難,看下面的代碼:

if (myValue == nil) { ...

如果程序員敲錯(cuò)成這樣:

if (myValue = nil) { ...

這是合法的語句,但是即使你是一個(gè)豐富經(jīng)驗(yàn)的程序員,即使盯著眼睛瞧上好多遍也很難調(diào)試出錯(cuò)誤。但是如果把 nil 放在左邊,因?yàn)樗荒鼙毁x值,所以就不會(huì)發(fā)生這樣的錯(cuò)誤。 如果程序員這樣做,他/她就可以輕松檢查出可能的原因,比一遍一遍查看敲下的代碼要好很多。

為了避免這些奇怪的問題,途徑是使用感嘆號(hào)來判斷。因?yàn)?nil 是 解釋到 NO 所以沒必要在條件語句里面把它和其他值比較。同時(shí),不要直接把它和 YES 比較,因?yàn)?YES 的定義是 1 而 BOOL 是 8 位的,實(shí)際上是 char 類型。

推薦:

if (someObject) { ...
if (![someObject boolValue]) { ...
if (!someObject) { ...

不推薦:

if (someObject == YES) { ... // Wrong
if (myRawValue == YES) { ... // Never do this.
if ([someObject boolValue] == NO) { ...

這樣同時(shí)也能提高一致性,以及提升可讀性。

黃金大道

當(dāng)編寫條件語句的時(shí)候,左邊的代碼間距應(yīng)該是一個(gè)“黃金”或者“快樂”的大道。 這是說,不要嵌套if語句。多個(gè) return 語句是OK的。這樣可以避免 Cyclomatic 復(fù)雜性,并且讓代碼更加容易閱讀。因?yàn)槟愕姆椒ǖ闹匾牟糠譀]有嵌套在分支上,你可以很清楚地找到相關(guān)的代碼。

推薦:

- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }

  //Do something important
}

不推薦:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

復(fù)雜的表達(dá)式

當(dāng)你有一個(gè)復(fù)雜的 if 子句的時(shí)候,你應(yīng)該把它們提取出來賦給一個(gè) BOOL 變量,這樣可以讓邏輯更清楚,而且讓每個(gè)子句的意義體現(xiàn)出來

BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear      = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession     = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
    // Do something very cool
}

三元運(yùn)算符

三元運(yùn)算符 ? 應(yīng)該只用在它能讓代碼更加清楚的地方。 一個(gè)條件語句的所有的變量應(yīng)該是已經(jīng)被求值了的。計(jì)算多個(gè)條件子句通常會(huì)讓語句更加難以理解,就像if語句的情況一樣,或者把它們重構(gòu)到實(shí)例變量里面。

推薦:

result = a > b ? x : y;

不推薦:

result = a > b ? x = c > d ? c : d : y;

當(dāng)三元運(yùn)算符的第二個(gè)參數(shù)(if 分支)返回和條件語句中已經(jīng)檢查的對(duì)象一樣的對(duì)象的時(shí)候,下面的表達(dá)方式更靈巧:

推薦:

result = object ? : [self createObject];

不推薦:

result = object ? object : [self createObject];

錯(cuò)誤處理

當(dāng)方法返回一個(gè)錯(cuò)誤參數(shù)的引用的時(shí)候,檢查返回值,而不是錯(cuò)誤的變量。

推薦:

NSError *error = nil;
if (![self trySomethingWithError:&error]) {
    // Handle Error
}

此外,一些蘋果的 API 在成功的情況下會(huì)對(duì) error 參數(shù)(如果它非 NULL)寫入垃圾值(garbage values),所以如果檢查 error 的值可能導(dǎo)致錯(cuò)誤 (甚至崩潰)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)