ObjC的BOOL爲何要用YES、NO而不建議用true、false?

原文地址:蘋果梨的博客html

嗯,昨天給本身挖了個坑,仍是早填坑早完事兒,因此今天有了這篇:bash

朋友,ObjC 的 BOOL 類型瞭解一下?函數

可能有人告訴你 BOOL 是 signed char 類型的。放在之前,這個答案是對的,可是放在如今就不徹底對了。接下來我來給你們一點點解釋其中的細節。ui

ObjC 的 BOOL 究竟是什麼類型?

當前個人 Xcode 版本是 9.3.1,BOOL 的定義是這樣的(有適當刪減):spa

#if TARGET_OS_OSX || (TARGET_OS_IOS && !__LP64__ && !__ARM_ARCH_7K)
# define OBJC_BOOL_IS_BOOL 0
#else
# define OBJC_BOOL_IS_BOOL 1
#endif

#if OBJC_BOOL_IS_BOOL
    typedef bool BOOL;
#else
# define OBJC_BOOL_IS_CHAR 1
    typedef signed char BOOL; 
#endif
複製代碼

做爲 iPhone 開發者(🙄),能夠近似的理解爲在 64-bit 設備上 BOOL 實際是 bool 類型,在 32-bit 設備上 BOOL 的實際類型是 signed char翻譯

YES / NO 是什麼?

那麼 YES / NO 又分別是什麼值呢?咱們看一下具體的定義:code

#if __has_feature(objc_bool)
#define YES __objc_yes
#define NO __objc_no
#else
#define YES ((BOOL)1)
#define NO ((BOOL)0)
#endif
複製代碼

這裏要先看一下 __objc_yes__objc_no 是什麼值,咱們在 LLVM 的文檔中能夠獲得答案:cdn

The compiler implicitly converts __objc_yes and __objc_no to (BOOL)1 and (BOOL)0. The keywords are used to disambiguate BOOL and integer literals.
複製代碼

__objc_yes__objc_no 其實就是 (BOOL)1(BOOL)0,這麼寫的緣由就是爲了消除 BOOL 和整型數的歧義而已。htm

(BOOL)1(BOOL)0 這個你們應該也都能很容易理解了,其實就是把 1 和 0 強轉成了 BOOL 對應的實際類型。blog

因此綜上所述爲了類型的正確對應,在給 BOOL 類型設值時要用 YES / NO

true / false 是什麼?

最先的標準 C 語言裏是沒有 bool 類型的,在 2000 年的 C99 標準裏,新增了 _Bool 保留字,而且在 stdbool.h 裏定義了 truefalsestdbool.h 的內容能夠參照這裏

#define bool _Bool
#define true 1
#define false 0
複製代碼

這裏只截取了標準 C 語言狀況下的定義(C++ 是自帶 bool 和 true、false 的)。能夠看到這裏只是定義了它們的值,可是卻沒有保證它們的類型,就是說 true / false 其實能夠應用在各類數據類型上。

有些人還提到 TRUE / FALSE 這兩個宏定義,它們其實不是某個標準定義裏的內容,通常是早年沒有標準定義時自定義出來替代 true / false 使用的,大部分狀況下他們的定義和 true / false 一致。

咱們能夠寫一段代碼來驗證下:

BOOL a = TRUE;
a = true;
a = YES;
複製代碼

使用 Xcode 的菜單進行預處理,展開宏定義:

16-A

而後咱們就能夠獲得展開後的結果:

BOOL a = 1;
a = 1;
a = __objc_yes;
複製代碼

爲何要對 BOOL 用 YES / NO 而不是 true / false?

能夠看到 ObjC 是本身定義了 BOOL 的類型,而後定義了對應要使用的值 YES / NO,理所固然的第一個緣由是咱們要按照標準來。

另外一方面,既然 ObjC 的 BOOL 使用的不是標準 C 的定義,那麼之後這個定義可能還會修改。雖說機率很低,可是畢竟從上面的代碼看就經歷了 signed charbool 的一次修改不是麼?爲了不這種風險,建議仍是要使用 YES / NO

在某些狀況下,類型不匹配會致使 warning,而 YES / NO 是帶類型的,能夠保證類型正確,因此建議要用 YES / NO

使用 BOOL 類型的注意點

由於 BOOL 類型在不一樣設備有不一樣的表現,因此有一些地方咱們要注意。

不要手賤寫 "== YES" 和 "!= YES"

在 BOOL 爲 bool 類型的時候,只有真假兩個值,實際上是能夠寫 "== YES" 和 "!= YES" 的。咱們先舉個例子:

BOOL a = 2;
if (a) {
    NSLog(@"a is YES");
} else {
    NSLog(@"a is NO");
}
if (a == YES) {
    NSLog(@"a == YES");
} else {
    NSLog(@"a != YES");
}
複製代碼

在 64-bit 設備咱們將獲得結果:

a is YES
a == YES
複製代碼

看上去沒什麼毛病,完美!

可是在 32-bit 設備咱們將獲得結果:

a is YES
a != YES
複製代碼

這是爲何呢?由於在 32-bit 設備上 BOOL 是 signed char 類型的。ObjC 對數值類型作 (a) 這種真假判斷時爲 0 則假、非 0 則真,因此咱們能夠獲得 a is YES 這種結果。可是對數值類型作 (a == YES) 這種判斷時邏輯是什麼樣的,想必不用我說你們也猜到了,代碼翻譯出來就是相似這樣的:

signed char a = 2;
if (a == (signed char)1) {
    NSLog(@"a == YES");
} else {
    NSLog(@"a != YES");
}
複製代碼

咱們固然只能獲得 a != YES 這樣的結果。

避免把超過 8-bit 的數據強轉成 BOOL

一樣在 64-bit 的設備上,也就是 bool 類型上不會有這個問題,可是在 signed char 類型上就會有這個問題。咱們先看代碼:

int a = 256;
if (a) {
    NSLog(@"a is YES");
} else {
    NSLog(@"a is NO");
}
BOOL b = a;
if (b) {
    NSLog(@"b is YES");
} else {
    NSLog(@"b is NO");
}
複製代碼

在 32-bit 設備上輸出結果:

a is YES
b is NO
複製代碼

是否是有點魔幻?可是緣由也驚人的簡單:

  • a 的二進制值爲 00000000 00000000 00000001 00000000
  • 轉換爲 signed char 類型的 b 時丟失了高位
  • b 的二進制值爲 00000000

因此千萬不要作這樣的蠢事,更常見的例子是一個 C 函數(十分直觀):

// 正確的用法
bool isDifferent(int a, int b) {
    return a - b;
}
// 錯誤的用法
signed char isDifferent(int a, int b) {
    return a - b;
}
複製代碼

總結

但願今天的介紹可讓你更深刻的瞭解 ObjC 的 BOOL 類型,當心點不要在代碼裏埋出大 bug 哦。

相關文章
相關標籤/搜索