條件語句

2018-08-12 21:19 更新

條件語句

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

推薦:

if (!error) {
    return success;
}

不推薦:

if (!error)
    return success;

或者

if (!error) return success;

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

代碼在這里:

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;
}

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

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

尤達表達式

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

Yoda

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

推薦:

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 表達式,nil 檢查的方式也是存在爭議的。一些 notous 庫 像這樣檢查對象是否為 nil:

if (nil == myValue) { ...

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

if (myValue == nil) { ...

如果程序員敲錯成這樣:

if (myValue = nil) { ...

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

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

推薦:

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

不推薦:

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

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

黃金大道

當編寫條件語句的時候,左邊的代碼間距應該是一個“黃金”或者“快樂”的大道。 這是說,不要嵌套if語句。多個 return 語句是OK的。這樣可以避免 Cyclomatic 復雜性,并且讓代碼更加容易閱讀。因為你的方法的重要的部分沒有嵌套在分支上,你可以很清楚地找到相關的代碼。

推薦:

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

  //Do something important
}

不推薦:

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

復雜的表達式

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

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

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

三元運算符

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

推薦:

result = a > b ? x : y;

不推薦:

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

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

推薦:

result = object ? : [self createObject];

不推薦:

result = object ? object : [self createObject];

錯誤處理

當方法返回一個錯誤參數(shù)的引用的時候,檢查返回值,而不是錯誤的變量。

推薦:

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號