不得不说在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是不支持宏定义的, 不过可使用全局的常量和全局函数来替换
一部分
宏的功能对象