iOS-深挖BOOL

為啥要深挖這玩意

你每天都在用BOOL吧吧凉?那我就來問一道題:請問BOOL是非0即真嗎?

如果不是百分百確定的踏志,請往下看阀捅。

BOOL的定義(Xcode7.3版本,位于usr/include/objc/objc中)

/// 位于<objc/objc>頭文件中

/// Type to represent a boolean value.
#if (TARGET_OS_IPHONE && __LP64__)  ||  TARGET_OS_WATCH
#define OBJC_BOOL_IS_BOOL 1
typedef bool BOOL;
#else
#define OBJC_BOOL_IS_CHAR 1
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

這是BOOL在SDK中的定義针余。

1. 讓我們展開宏定義來看看:

TARGET_OS_IPHONETARGET_OS_WATCH的定義是在:

TARGET_OS_IPHONE定義

請注意一點饲鄙,iOS 9.3只是其中的一個SDK,代表的是真機iOS 9.3版本的SDK圆雁,在Xcode中有很多的SDK忍级,如:

SDK種類

查看不同的SDK,你會發(fā)現(xiàn)TARGET_OS_IPHONETARGET_OS_WATCH所定義的值是不同的伪朽。如iOS 9.3中:

#define TARGET_OS_MAC               1
#define TARGET_OS_WIN32             0
#define TARGET_OS_UNIX              0
#define TARGET_OS_IPHONE            1 
#define TARGET_OS_IOS               1
#define TARGET_OS_WATCH             0
#define TARGET_OS_TV                0
#define TARGET_OS_SIMULATOR         0
#define TARGET_OS_EMBEDDED          1 

而在OS X 10.11中:

#define TARGET_OS_MAC               1
#define TARGET_OS_WIN32             0
#define TARGET_OS_UNIX              0
#define TARGET_OS_IPHONE            0 
#define TARGET_OS_IOS               0
#define TARGET_OS_WATCH             0
#define TARGET_OS_TV                0
#define TARGET_OS_SIMULATOR         0
#define TARGET_OS_EMBEDDED          0 

這兩個宏定義代表的含義同時也說明了在不同的平臺/環(huán)境下轴咱,部分判斷并不是靠“機器”自動進行的,而是靠人為判斷并定義的烈涮,所以程序并沒有那么神奇朴肺,它并不能夠自動區(qū)分iPhone還是iWatch。

__LP64__則是由預(yù)處理器定義的宏坚洽,代表當前操作系統(tǒng)是64位戈稿。該宏在頭文件中無法找到的,但是我們還是有方法可以查看的:

a. 可以在終端中輸入cpp -dM /dev/null進行查看(當然常規(guī)手段只能在Mac上運行該指令):

__LP64__定義1

b. 在代碼中對__LP64__進行 if 判斷讶舰,如:

#if __LP64__
    #define kNum 64
#else
    #define kNum 32
#endif
    NSLog(@"kNum: %d",kNum);

分別選擇iPhone5/5c/4s/4等真機和iPhone6/6s等進行運行查看結(jié)果(iPhone模擬器也能得出正確值鞍盗,但非“真實值”)。

c. 通過Xcode助理器的預(yù)編譯功能查看__LP64__的預(yù)編譯結(jié)果:

__LP64__定義3

2. 偽代碼解釋:

if (處于64位iPhone 或者 iWatch) {
  BOOL就是bool (非0即真)
} else {
  BOOL就是signed char (1個字節(jié)跳昼。-128 ~ 127)
}

這里的else般甲,最常見的就是32位CPU的iPhone,如iPhone5c/5/4s/4等庐舟。
定義里可以看出欣除,BOOL其實是個宏,那么知道了BOOL背后的數(shù)據(jù)類型挪略,出了問題就有能力去分析問題历帚。

3. 這里就引申出一個可能存在的問題:

- (void)doSomeThingWithA:(NSInteger)a {
    NSInteger b = 200;
    if (a + b) {
        // 業(yè)務(wù)代碼
    }
}

乍看之下,這段代碼只要 a != -200杠娱,都是能夠執(zhí)行業(yè)務(wù)代碼部分挽牢。在64位iPhone和iWatch機器里確實是如此,因為定義的BOOL就是bool摊求,就是非0即真禽拔,你用負數(shù)也是真。
但是在32位機器里呢?如果a傳入56睹栖,a + b == 256硫惕,二進制是 0000 0000 0000 0000 0000 0001 0000 0000(因為32位里NSInteger是int,占用4個字節(jié)野来,每個字節(jié)8位)恼除。該數(shù)值在if時會強轉(zhuǎn)為BOOL(也就是sign char)進行判定:取低8位 0000 0000 來判斷,而0000 0000就是0曼氛。if中判定為0豁辉,則為假,不再執(zhí)行業(yè)務(wù)代碼舀患。

YES / NO的定義

說到了BOOL徽级,就要說說YES / NO。

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

這兩玩意也是宏聊浅。這里定義了YES / NO在不同條件下的真實類型餐抢。

1. 讓我們展開宏定義來看看:

__has_feature這個宏是clang提供的一個宏,用來指定在當前語言下是否支持某個“特征”狗超。上述的objc_bool這個特征暫時還沒有找到具體的定義弹澎,因此讓我們來用代碼檢測下__has_feature(objc_bool)這個條件是否成立。

#if __has_feature(objc_bool)
    #define kHaveFeature 10
#else
    #define kHaveFeature 20
#endif
    
    NSLog(@"kHaveFeature : %d", kHaveFeature);
    
    NSLog(@"__objc_yes : %d",__objc_yes);
    NSLog(@"__objc_no : %d",__objc_no);
    
    NSLog(@"YES : %d",YES);
    NSLog(@"NO : %d",NO);

讓我們用Xcode助理器的預(yù)編譯功能查看這段代碼的預(yù)編譯結(jié)果:

YES/NO的定義

經(jīng)驗證努咐,32位與64位真機的預(yù)編譯結(jié)果均如圖所示苦蒿。也就是說當前的iOS系統(tǒng)中,YES與NO的實際類型為__objc_yes__objc_no渗稍。

2. __objc_yes__objc_no到底又是什么佩迟?

但是問題又來了,翻遍usr/include目錄竿屹,還是找不到__objc_yes的定義报强,那么它們又是個什么鬼?讓我們借助編譯器來看看它的類型:

  • 32位下的__objc_yes__objc_no
32位下的__objc_yes
  • 64位下的__objc_yes__objc_no
64位下的__objc_yes

盡管我們無法從SDK的頭文件里獲取__objc_yes__objc_no的真實類型拱燃,但是我們的編譯器還是幫助我們找到了它們的真實類型:BOOL秉溉。

再讓我們實際打印出這兩者的值來看看:

NSLog(@"__objc_yes : %d",__objc_yes);
NSLog(@"__objc_no : %d",__objc_no);
    
NSLog(@"YES : %d",YES);
NSLog(@"NO : %d",NO);

// 結(jié)果:32位與64位均為:
2016-12-29 20:15:20.321 NewTestBOOL[3574:641831] __objc_yes : 1
2016-12-29 20:15:20.322 NewTestBOOL[3574:641831] __objc_no : 0
2016-12-29 20:15:20.324 NewTestBOOL[3574:641831] YES : 1
2016-12-29 20:15:20.325 NewTestBOOL[3574:641831] NO : 0

3. 這里又引申出一個可能存在的問題:

請問下列代碼會打印哪些結(jié)果?

BOOL b2 = 2;
NSLog(@"b2 的值 : %d",b2);
if (b2) {
    NSLog(@"b2 為真");
} else {
    NSLog(@"b2 為假");
}
if (b2 == YES) {
    NSLog(@"b2 等于 YES ");
} else {
    NSLog(@"b2 不等于 YES ");
}
  1. b2的值 : 2
  2. b2 為真
  3. b2 不等于 YES

這個答應(yīng)在32位與64位下應(yīng)該沒問題了~那讓我們實際跑起來看看:

  • 32位下:

    2016-12-29 20:44:08.515 NewTestBOOL[3600:645491] b2 的值 : 2
    2016-12-29 20:44:08.516 NewTestBOOL[3600:645491] b2 為真
    2016-12-29 20:44:08.517 NewTestBOOL[3600:645491] b2 不等于 YES 
    

    沒毛病~

  • 64位下:

2016-12-29 20:45:03.546554+0800 NewTestBOOL[3337:1547022] b2 的值 : 1
2016-12-29 20:45:03.546564+0800 NewTestBOOL[3337:1547022] b2 為真
2016-12-29 20:45:03.546594+0800 NewTestBOOL[3337:1547022] b2 等于 YES 

有毛餐胗召嘶!

讓我們仔細分析下64位為何b2會變成1:

64位下b2的類型是BOOL,真實類型為bool哮缺,任何不為0的數(shù)強轉(zhuǎn)為bool類型弄跌,均為轉(zhuǎn)為true,在C99的官方定義中尝苇,true為1铛只,所以這里b2變?yōu)榱?埠胖。當然這里還有個疑問:為何不為0的數(shù)轉(zhuǎn)為bool均為true?不要告訴我“就是這樣的”淳玩?如果有人知道可以聯(lián)系我~(475325435@qq.com

擴展內(nèi)容

我們來看看C99中的bool定義:

#ifndef __STDBOOL_H
#define __STDBOOL_H

/* Don't define bool, true, and false in C++, except as a GNU extension. */
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
/* Define _Bool, bool, false, true as a GNU extension. */
#define _Bool bool
#define bool  bool
#define false false
#define true  true
#endif

#define __bool_true_false_are_defined 1

#endif /* __STDBOOL_H */

這是stdbool.h中的bool的定義(C99標準)直撤。
bool其實還是個宏,其真實類型為_Bool蜕着。_Bool在C99中是作為C語言原生的布爾類型的谊惭,也就是和int/float一樣是在編譯器中預(yù)定義的,1為真侮东,0為假,具體占用內(nèi)存大小還是由編譯器決定豹芯。定義中的true與false分別為1和0悄雅。

總結(jié)

宏真的是個好東西~


更新:2017年4月16日

當前最新的iOS版本為10.3.1,Xcode最新版本為8.3.1铁蹈。

很遺憾Steve Jobs的經(jīng)典iPhone4在默認的Xcode8中無法真機調(diào)試(連線調(diào)試)宽闲,但是還是可以打包進行安裝運行的,只不過可能查看日志略麻煩~需要支持iOS7的同學們可能要多費些力握牧。

當前10.3 SDK中已經(jīng)對BOOL的定義作了少許改動容诬,讓我們來瞧瞧:

/// Type to represent a boolean value.

#if defined(__OBJC_BOOL_IS_BOOL)
    // Honor __OBJC_BOOL_IS_BOOL when available.
#   if __OBJC_BOOL_IS_BOOL
#       define OBJC_BOOL_IS_BOOL 1
#   else
#       define OBJC_BOOL_IS_BOOL 0
#   endif
#else
    // __OBJC_BOOL_IS_BOOL not set.
#   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
#endif

#if OBJC_BOOL_IS_BOOL
    typedef bool BOOL;
#else
#   define OBJC_BOOL_IS_CHAR 1
    typedef signed char BOOL; 
    // BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
    // even if -funsigned-char is used.
#endif
  1. OBJC_BOOL_IS_BOOL現(xiàn)在默認先判斷預(yù)編譯器的定義。若預(yù)編譯器未定義該宏沿腰,再進行平臺判斷览徒。

  2. 這里特別強調(diào)了TARGET_OS_OSX這個條件,即Mac OS操作系統(tǒng)下颂龙,BOOL為signed char類型习蓬。其實在早前的Xcode7.3中,TARGET_OS_OSX也是將BOOL定義為signed char措嵌,至于蘋果在普遍為64位的Mac里不使用bool來作為BOOL的真實類型躲叼,還不得而知,如果你知道企巢,可以告訴我~

  3. __ARM_ARCH_7K這個宏枫慷,應(yīng)該是在Clang和LLVM中定義的。搜索LLVM4.0源碼浪规,能得到的信息只有:

    // Unfortunately, __ARM_ARCH_7K__ is now more of an ABI descriptor. The CPU
    // happens to be Cortex-A7 though, so it should still get __ARM_ARCH_7A__.
    if (getTriple().isWatchABI())
      Builder.defineMacro("__ARM_ARCH_7K__", "2");
    

    據(jù)悉iWatch的CPU指令集為armv7k或听,由該定義推測,__ARM_ARCH_7K應(yīng)該是代表手表的宏罗丰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末神帅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子萌抵,更是在濱河造成了極大的恐慌找御,老刑警劉巖元镀,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霎桅,居然都是意外死亡栖疑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門滔驶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遇革,“玉大人,你說我怎么就攤上這事揭糕÷芸欤” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵著角,是天一觀的道長揪漩。 經(jīng)常有香客問我,道長吏口,這世上最難降的妖魔是什么奄容? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮产徊,結(jié)果婚禮上昂勒,老公的妹妹穿的比我還像新娘。我一直安慰自己舟铜,他們只是感情好戈盈,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谆刨,像睡著了一般奕谭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痴荐,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天血柳,我揣著相機與錄音,去河邊找鬼生兆。 笑死难捌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的鸦难。 我是一名探鬼主播根吁,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼合蔽!你這毒婦竟也來了击敌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤拴事,失蹤者是張志新(化名)和其女友劉穎沃斤,沒想到半個月后圣蝎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡衡瓶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年徘公,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哮针。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡关面,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出十厢,到底是詐尸還是另有隱情等太,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蛮放,位于F島的核電站澈驼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏筛武。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一挎塌、第九天 我趴在偏房一處隱蔽的房頂上張望徘六。 院中可真熱鬧,春花似錦榴都、人聲如沸待锈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竿音。三九已至,卻和暖如春拴驮,著一層夾襖步出監(jiān)牢的瞬間春瞬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工套啤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宽气,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓潜沦,卻偏偏與公主長得像萄涯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唆鸡,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉涝影,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評論 0 9
  • 本文詳細整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼争占,具備了靈活的...
    lylaut閱讀 792評論 0 4
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 748評論 0 1
  • (兩年前第一次填詞燃逻,學習東坡體也不協(xié)律) 十年柔情已渺茫 不曾想序目,怎能忘 一葉扁舟江湖留過往 千載風雨打頭墻 再相...
    衣吹風閱讀 268評論 3 2
  • 之所以寫下如此題目,是想提醒并鞭策自己在新年的第一個春日里重新打造全新的自我唆樊。無論從精神面貌還是思想宛琅,以及思維方式...
    官小姐不當官閱讀 160評論 2 0