Objective-C中的預處理器指令與宏

什么是預處理器,跟我有什么關系?

預處理器是在OC源文件編譯過程中的一個部分,而且是第一個處理部分,預處理器的預也由此可見。

整個編譯過程可以大致分為:預處理器進行詞法分析 -> 語法分析 -> 生成代碼和優(yōu)化 -> 生成可執(zhí)行的二進制文件。

既然有這么多過程,為什么要關注預處理器呢?因為它在我們的開發(fā)中最常見,而且每個iOS開發(fā)者一定都見過。

不信的話我們可以列舉一下常見的預處理指令,預處理器有其區(qū)別于Objective-C的獨特語法,語法形式如下:

#指令名 指令參數(shù)

有點眼熟了?我們再具體地說說包含哪些:

  • 頭文件包含(#include、#import)
  • 條件編譯(#if、#elif、#else、#endif、#ifdef和#ifndef)
  • 診斷(#error、#warning和#line)
  • pragma指令

這樣列出來就明白了吧,早說是這些就簡單了嘛,大部分都是熟人,慢著,這些熟悉的具體表示什么?有什么區(qū)別?那些不太熟的又是干什么的呢?我們一個個來看。

除了上述的指令外,還有一個老熟人也屬于預處理器的范疇,下文再來說。

預處理器指令

頭文件包含

學C語言的時候就接觸到了#include,學java也會用到import(注意沒有#號),都是用來導入頭文件的,這個作用我們明白,OC中的導入頭文件有#include和#import兩種指令,而且對于頭文件名還分為雙引號包含和尖括號包含兩種方式:

#include "頭文件名"

#include <頭文件名>

#import "頭文件名"

#import <頭文件名>

問題來了,有啥區(qū)別?

先說雙引號和尖括號的區(qū)別,雙引號封裝頭文件名時,會先從存儲要編譯的這個文件的目錄中去搜索包含的頭文件,找不到再去用來搜索系統(tǒng)標準頭文件的默認目錄搜索。而尖括號封裝頭文件名時,會直接去用來搜索系統(tǒng)標準頭文件的默認目錄搜索。由此可見,要用尖括號封裝標準頭文件,而自己寫的OC類頭文件,應該用雙引號封裝。

而對于#include和#import這兩者,區(qū)別在于#import可以確保頭文件只被引用一次,這樣就可以防止遞歸包含,什么叫遞歸包含,A引用B和C,B也引用了C,那就都包含了C,這就重復包含了。因此,如果非要用#include,那必須額外地寫指令來判斷有沒有包含過,來避免遞歸包含。

條件編譯

條件編譯特別像我們在所有編程語言中都能看到的 if ... else if ... else 形式,也就是條件判斷語句。

用法如下
#if(對應于if)
// 執(zhí)行內(nèi)容
#elif(對應于else if)
// 執(zhí)行內(nèi)容
#else(對應于else)
// 執(zhí)行內(nèi)容
#endif

對于各個語句的用法要求也和一般語言相同,特殊的是最底下有一個#endif,畢竟沒有大括號也沒有縮進嘛,而且支持嵌套操作,那嵌套的界限就更要靠#endif來判斷了對吧。

除了這些以外,還有兩個:

#ifdef 宏名
// 執(zhí)行內(nèi)容
#endif

#ifndef 宏名
// 執(zhí)行內(nèi)容
#endif

其中的def是define的簡寫,ndef也就是not define,很容易猜到意思,分別就是判斷是否定義過后面跟著的宏。同樣的要用#endif來作為結束的界限。

診斷

診斷中先說頭兩個:

#ifndef 宏名
#error "發(fā)生錯誤啦"
#endif

#if XXX
#warning "警報!警報!"
#endif

一般都用在條件判斷語句內(nèi)容中,后面都跟著雙引號帶著的消息,error指令會直接中止編譯,拋出錯誤消息,warning也會拋出警告消息,但不會中止編譯。

第三種診斷指令:

#line 行號 "文件名"
//假設這里有一行會發(fā)生錯誤的代碼

這個指令理解起來有些復雜,首先line定義了一個行號,那么之后每一行都會有一個在此基礎上依次加一的行號,比如下一行的錯誤代碼就是第11行。發(fā)生錯誤后,會拋出說"文件名"文件的第11行有錯誤。后面跟著的文件名是一個可選項,寫了就可以在消息中顯示,不寫也沒關系。

#pragma指令

這個指令更常見了,我們使用UITableView的時候,經(jīng)常會用到:

#pragma mark - UITableView DataSource
……
#pragma mark - UITableView Delegate
……

這個#pragma mark指令可以在Xcode 中的該文件的方法列表中插入標記,#pragma mark -就可以插入一個分隔線,后跟文字就可以插入文字標簽。

除此之外,#pragma指令還包含很多別的選項,上面的是用的最多的,其他的可以查看文檔。

預處理器之宏

要知道,宏也是預處理器范疇內(nèi)的內(nèi)容,我們用的也很多:

// 定義常量值
#define 宏名 值
//定義函數(shù)宏
#define 宏名(參數(shù)) 代碼

// 移除宏
#undef 宏名

宏被定義后,會一直存在,并且能在整個文件中起作用,直到被#undef指令移除為止。如果函數(shù)有多個參數(shù),用逗號分隔開。

定義函數(shù)宏的時候,有一個細節(jié)要注意,就是要多對參數(shù)使用括號:

#defind SQUARE(x) ((x) * (x))

為什么要這么麻煩?為什么不能直接 x * x?要知道,宏在這個意義上是很“傻”的,它只會單純的將你輸入的x值拿去替換函數(shù)代碼中的x,并不會做什么處理,所以如果你這樣輸入就會造成沒有意料到的結果:

#defind SQUARE(x) x * x

int number = SQUARE(4+2);// 你以為會等于36?并不會

// 我們說了,宏只會簡單替換,所以上面等價于:

int number = 4 + 2 * 4 + 2;// 其實等于14

知道問題所在了吧,這很嚴重,因為不知道的話根本無法理解這個bug為什么會出現(xiàn),所以都應該使用括號。此外,如果你的代碼有多行,還應該使用大括號括起來:

#define FUNC(a, b) {a = a + b; b = a - b;}

此外,不要過度使用宏!宏很強大,也很危險,出了問題往往難以診斷,也不好維護。

以上就是OC編譯中的預處理器中的一些預處理語言函數(shù)的內(nèi)容,預處理器的內(nèi)容當然不單單只有這些,還有對源文件的一些處理,但這些是我們平常開發(fā)中經(jīng)常遇到的,了解他們是必須且重要的。


查看作者首頁

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 目錄 一.預處理的工作方式... 3 1.1.預處理的功能... 3 1.2預處理的工作方式... 3 二.預處理...
    朱森閱讀 1,551評論 0 2
  • C中的預編譯宏定義 2009-02-10 作者: infobillows 來源:網(wǎng)絡 在將一個C源程序轉換為可執(zhí)行...
    白水灬煮一切閱讀 1,739評論 0 5
  • 在實際開發(fā)中,有時候在編譯之前還需要對源文件進行簡單的處理。例如,我們希望自己的程序在Windows和Linux下...
    凡眼觀世界閱讀 983評論 1 0
  • 對你最沉默的時候 是以為你會暗暗關心 結果卻是一通氣的發(fā)泄
    小小紙飛機閱讀 217評論 0 0
  • 即將進入耄耋之年、身體也依舊健康的瓊瑤阿姨,前幾天寫下了“生前遺囑”,以公開信的形式,表達了“尊嚴死”的意愿。她甚...
    Runningapple閱讀 406評論 0 0

友情鏈接更多精彩內(nèi)容