Objective-C預處理器不是編譯器的一部分,而是編譯過程中的一個單獨步驟。 簡單來說,Objective-C預處理器只是一個文本替換工具,它指示編譯器在實際編譯之前進行必要的預處理。 我們將Objective-C預處理器稱為OCPP。
所有預處理器命令都以井號(#
)開頭。它必須是第一個字元(前面不能有空格),並且為了便於閱讀,預處理器指令應該從第一列開始。 以下部分列出了所有重要的預處理程式指令 -
編號 | 指令 | 描述 |
---|---|---|
1 | #define |
替換預處理器宏 |
2 | #include |
從另一個檔插入特定標頭 |
3 | #undef |
未定義的預處理器宏 |
4 | #ifdef |
如果定義了此宏,則返回true |
5 | #ifndef |
如果未定義此宏,則返回true |
6 | #if |
測試編譯時條件是否為true |
7 | #else |
#if 的替代方案 |
8 | #elif |
在#else 和 #if 中的一個語句 |
9 | #endif |
結束預處理器條件 |
10 | #error |
在stderr 上列印錯誤消息 |
11 | #pragma |
使用標準化方法向編譯器發出特殊命令 |
1. 預處理器示例
分析以下示例以瞭解各種宏的指令。
#define MAX_ARRAY_LENGTH 20
該指令告訴OCPP 用20
替換MAX_ARRAY_LENGTH
宏標識。使用#define
定義常量來提高可讀性。
#import <Foundation/Foundation.h>
#include "myheader.h"
這些指令告訴OCPP 從Foundation Framework獲取foundation.h
,並將文本添加到當前原始檔案中。 下一行告訴OCPP 從本地目錄獲取myheader.h
並將內容添加到當前原始檔案。
#undef FILE_SIZE
#define FILE_SIZE 42
它告訴OCPP 取消定義現有的FILE_SIZE
,並將FILE_SIZE
定義為42
。
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
它告訴OCPP僅在尚未定義MESSAGE
宏時才定義MESSAGE
。
#ifdef DEBUG
/* Your debugging statements here */
#endif
它告訴OCPP如果定義了DEBUG
,則執行包含語句的過程。 如果在編譯時將- DDEBUG
標誌傳遞給gcc
編譯器,這將非常有用。 它將定義DEBUG
,因此可以在編譯期間動態打開和關閉調試。
2. 預定義的宏
ANSI C定義了許多宏。雖然每個都可用於編程,但不應直接修改預定義的宏。
編號 | 宏 | 描述 |
---|---|---|
1 | __DATE__ |
當前日期為“MMM DD YYYY” 格式的字元文字 |
2 | __TIME__ |
當前時間作為“HH:MM:SS” 格式的字元文字 |
3 | __FILE__ |
它包含當前檔案名作為字串文字。 |
4 | __LINE__ |
它包含當前行號作為十進位常量。 |
5 | __STDC__ |
當編譯器符合ANSI標準時,定義為1 。 |
試試下麵的例子代碼 -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"File :%s\n", __FILE__ );
NSLog(@"Date :%s\n", __DATE__ );
NSLog(@"Time :%s\n", __TIME__ );
NSLog(@"Line :%d\n", __LINE__ );
NSLog(@"ANSI :%d\n", __STDC__ );
return 0;
}
當編譯並執行檔main.m
中的上述代碼時,它會產生以下結果 -
2018-11-15 08:44:54.041 main[50640] File :main.m
2018-11-15 08:44:54.042 main[50640] Date :Nov 15 2018
2018-11-15 08:44:54.042 main[50640] Time :08:44:52
2018-11-15 08:44:54.042 main[50640] Line :7
2018-11-15 08:44:54.043 main[50640] ANSI :1
3. 預處理器運算符
Objective-C預處理器提供以下運算符來創建宏 -
3.1. 宏延續(\)
宏通常必須包含在一行中。宏延續運算符用於繼續對於單行來說太長的宏。 例如 -
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
3.2. 字串化(#)
字串化或數字符號運算符(#
)在宏定義中使用時,將宏參數轉換為字串常量。 此運算符只能在具有指定參數或參數列表的宏中使用。 例如 -
#import <Foundation/Foundation.h>
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
執行上面示例代碼,得到以下結果:
2018-11-15 08:56:38.088 main[98681] Carole and Debra: We love you!
3.3. 令牌粘貼(##)
宏定義中的令牌粘貼運算符(##
)組合了兩個參數。 它允許將宏定義中的兩個單獨的標記連接到一個標記中。 例如 -
#import <Foundation/Foundation.h>
#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
執行上面示例代碼,得到以下結果:
2018-11-15 08:58:04.872 main[138839] token34 = 40
它是如何發生的,因為這個例子導致預處理器的以下實際輸出 -
NSLog (@"token34 = %d", token34);
此示例顯示令牌##n
到token34
的串聯,這裏使用了stringize
和token-pasting
。
3.4. defined()運算符
預處理器定義的運算符用於常量運算式,以確定是否使用#define
定義識別字。如果定義了指定的識別字,則該值為true
(非零)。 如果未定義符號,則值為false
(零)。 定義的運算符指定如下 -
#import <Foundation/Foundation.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
NSLog(@"Here is the message: %s\n", MESSAGE);
return 0;
}
執行上面示例代碼,得到以下結果:
2018-11-15 09:04:30.790 main[31654] Here is the message: You wish!
4. 參數化宏
OCPP的一個強大功能是使用參數化宏模擬函數的能力。 例如,可能需要一些代碼來對數字進行平方,如下所示 -
int square(int x) {
return x * x;
}
可以使用宏重寫上面的代碼,如下所示 -
#define square(x) ((x) * (x))
必須先使用#define
指令定義帶參數的宏,然後才能使用它們。 參數列表括在括弧中,並且必須緊跟宏名稱。 宏名稱和左括弧之間不允許有空格。 例如 -
#import <Foundation/Foundation.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
執行上面示例代碼,得到以下結果:
2018-11-15 09:08:15.586 main[64146] Max between 20 and 10 is 20