不得不說在C系語言(C, Objective-C, C++...)中宏(macro)是個強大的東西, 雖然在基本的語法上面看上去是很是的簡單, 不過有時候正由於他的強大和方便, 就會致使在使用的時候, 其中會有不少的注意點, 若是不當心被忽略, 那麼將會帶來徹底不想要的結果. 因此要想靈活的使用它, 那麼仍是先了解一些比較好. 並且在iOS開發中若是你是使用OC, 那麼你可能常常會使用到#define(swift當前不支持宏)swift
首先扔出幾個宏的定義,調用這些宏的時候分別是什麼結果, 看看你可以在不看後面的狀況下, 清楚多少, 固然, 若是很清楚, 天然能夠忽略後文的八卦了..., 由於, 你絕對比我更瞭解宏...xcode
#define PI 3.14
#define log(x) printf("this is test: x = %d", x)
函數
#define log(x) printf("this is test: "#x" = %d", x)
測試
#define power(x) x*x
#define RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define print(...) printf(__VA_ARGS__)
this
#define RGB(r, g, b) {\ RGBA(r, g, b, 1.0f);\ }
spa
#define weakify( x ) autoreleasepool{} __weak typeof(x) weak##x = x;
code
#define weakify(...) \\ autoreleasepool {} \\ metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
#include
, 或者oc中的#import
, #define
...不少(條件編譯語句...)換行
的做用), 天然, #define只會容許定義一行的宏, 不過正由於上面提到的預處理以前會刪除反斜線和換行符的組合, 因此能夠利用反斜線定義多行宏
, 在刪除反斜線和換行符的組合後, 邏輯上就成了一行的宏了#define
, 宏的名字
, 主體
例如第一個宏#define PI(宏的名字) 3.14(主體)
, 這裏有個注意點就是, 宏的命名和普通的變量命名規則相同#define PI 3.14
這是宏的最簡單的定義了, 可能也是你們應用最廣的, 就是使用宏來定義一些常量(消除魔法數字
)或字符串..., 這一類能夠被稱爲類對象宏, 方便代碼閱讀和修改, 使用的時候直接使用定義的宏的名字, PI, 那麼預處理器就會將代碼中的PI替換爲3.14float computeAreaWithRadius(float r) {
return PI * r * r;
}複製代碼
#define log(x) printf("this is test: x = %d", x)
這是宏的第二類定義, 即類函數宏, 這一類的宏和函數相似的寫法, ( )
中能夠寫變量, 用做函數的參數, 不過, 這個和函數的區別是, 宏的參數不指定類型, 具體的參數類型在調用宏的時候由傳入的參數決定(有點其餘語言裏的泛型的意思), 這個能夠算是和函數相比的優勢, 下面測試一些這個宏的使用, 結果你猜對了麼?cdn
#define log(x) printf("this is test: x = %d", x)
int main(int argc, const char * argv[]) {
int y = 12;
log(y); // 輸出爲 this is test: x = 12
}複製代碼
#define log(x) printf("this is test: "#x" = %d", x)
, 這個定義中和上面的區別是使用了一個#運算符, #運算符被用於利用宏參數建立字符串, 區分一下和上面的結果#define log(x) printf("this is test: "#x" = %d", x)
int main(int argc, const char * argv[]) {
int y = 12;
log(y);
// 輸出爲 this is test: y = 12 (而不是 x = 12, 或者 12 = 12)
// 由於使用#和參數結合能夠被替換爲宏參數對應的字符串, "#x"表示字符串x, 這裏輸入的參數爲y, 則替換爲y(不是12)
log(2+4)// 輸出爲 this is test: 2+4 = 6
}複製代碼
#define power(x) x*x
這個和上面同樣是一個類函數宏, 這裏我本來的意願是計算 x*x即x的平方的值, 不過這樣的定義宏在有些狀況下是會出問題的, 這個例子就是告訴你們定義類函數宏的時候就真的要當心, 否則客人結果並非咱們預期的#define power(x) x*x
int x = 2;
int pow1 = power(x); // pow1 = 2*2 = 4
int pow2 = power(x+1); // pow2 = 3 * 3 = 9 ??
// 顯然對於pow1 = 4是沒有問題的
// 不過對於pow2 = 9 這個結果是有問題的, 定義的宏並無達到咱們預想的效果 結果爲 3*3
// 由於: 上面提到過宏是直接的代碼替換, 這裏宏展開後就成爲了 x+1*x+1 = 2+1*2+1 = 5
// 這裏由於運算優先級的緣由致使結果的不同, 因此pow應該(加上括號)定義爲
#define power(x) (x)*(x)複製代碼
#define RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
這裏是個簡單的多參數的類函數宏的定義, 這個宏在使用OC開發的時候 你們可能都會喜歡使用#define RGB(r, g, b) {\ RGBA(r, g, b, 1.0f);\ }
這個宏是一個"多行宏"定義的示例, 即在除了最後一行的最後加上反斜線(由於反斜線和換行符的組合在預編譯以前會被系統刪除), 同時這個宏也說明了, 宏的定義是能夠嵌套的(有些編譯器可能不支持, xcode中是支持的...) #define print(...) printf(__VA_ARGS__)
這個宏使用了兩個新的東西...
和__VA_ARGS__
, 這兩個是用來定義可變參數宏的, 能夠看到是很簡單的, 惟一一個注意點就是, ...
要放在參數的最後, 若是你使用C定義可變參數的函數就會發現過程就很複雜了#define print(...) printf(__VA_ARGS__)
int main(int argc, const char * argv[]) {
print("測試可變參數 ---- %d", 12); // 輸出結果爲: 測試可變參數 ---- 12
}複製代碼
#define weakify( x ) autoreleasepool{} __weak typeof(x) weak##x = x;
最後一個宏介紹另一個運算符 ##
這個是宏定義中的鏈接運算符, 例如上面的weak##x 就是將weak和參數x鏈接在一塊兒, 同時這一個宏在iOS開發中是頗有用的, 使用block的時候爲了消除循環引用 一般使用weakSelf, 那麼就能夠定義這樣一個宏, 而不用每次都輸入上面一段重複的代碼 __weak typeof(self) weakself = self
, 那麼上面定義的宏和這段代碼同樣會生成一個弱引用的新變量, 不過上面定義的時候使用了autoreleasepool{}
, 這一個自動釋放池本質上並無什麼用, 只不過對調用weakify會有影響, 須要使用@weakify(x), 😄看上去逼格更高, 不過在RAC中weakify是另外的方式定義的, (開篇給出的第九個宏定義)這個就能夠本身下去研究一下了.#define weakify( x ) autoreleasepool{} __weak typeof(x) weak##x = x;
加上 autoreleasepool{}使用宏的時候就應該加上@
像這樣:
- (void)delay {
@weakify(self)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakself test];
});
}
固然若是你沒有加autoreleasepool{}, 使用宏就不用加上@了
#define weakify( x ) __weak typeof(x) weak##x = x;
像這樣:
- (void)delay {
weakify(self)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakself test];
});
}複製代碼
😄這裏關於宏的介紹就先這樣了, 使用宏不少時候可讓咱們的代碼更容易閱讀和修改, 同時也能夠少寫不少的重複代碼, 但願你在使用C系語言開發的時候可以好好利用這個方便的東西, 若是你使用OC開發iOS那麼宏對你而言也會是一大福利, 若是使用swift開發iOS, 那麼... 目前swift是不支持宏定義的, 不過可使用全局的常量和全局函數來替換
一部分
宏的功能對象