OC 宏入门

来源:互联网 发布:如何做免费网络推广 编辑:程序博客网 时间:2024/06/05 05:10

宏是什么

宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。宏定义和操作符的区别是:宏定义是替换,不做计算,也不做表达式求解。

宏定义的种类

1. 对象宏: (不带参数)

    [0]#define [1]SIZE   [2]10     

[0]#define 定义宏 undef 取消宏定义

[1] 宏名称, 通常用大写字母

[2] 宏定义的内容,在编译期要替换宏名字的内容

2. 函数宏:(带参数)

    #define ADD(x, y)   x + y    

Demo

 #define ADD(x, y)   x + y# define SIZE 10- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    int size = SIZE + 10;    NSLog(@"size = %d", size);    int result = ADD(1, 2);    NSLog(@"result = %d", result);}

通过xcrun -sdk iphonesimulator clang -rewrite-objc 后我们可以看一下代码转化后的结果

   int size = 10 + 10;    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_032276_mi_0, size);    int result = 1 + 2;    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_032276_mi_1, result);

从上面的结果中可以看出。这里编译器只是进行了替换。

宏函数并非平常的函数操作,其本质还是代码块的替换。

看一个交换 a, b 两个值的宏

#define YM_Swap(_a_, _b_) { __typeof__(_a_) _t_ = _a_; (_a_) = (_b_); (_b_) = (_t_); }    int left_number = 10;    int right_number = 100;    YM_Swap(left_number, right_number);    NSLog(@"left_number = %d, right_number = %d",left_number, right_number);

转化后的结果:

  int left_number = 10;  int right_number = 100;    { int _t_ =  left_number; (left_number) = (right_number); (right_number) = (_t_); };    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_646835_mi_0,left_number, right_number);

从上面的结果来看,只是代码的替换,而不是作为一个函数去执行。这就是为什么有人疑问为什么宏能把两个数正真交换,而普通的函数只是参数的交换。

    int totalNumber = SIZE - ADD(2, 3);    NSLog(@"totalNumber = %d", totalNumber);  // 5 还是11

rewrite 后的代码

    int totalNumber = 10 - 2 + 3;    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_90b4d5_mi_0, totalNumber);

结果是11,还是代码替换。那如果我们想要实现 10 - 5 怎么办?
最简单的方法就是给 2 + 3 放在一个小括号里面了

 #define ADD(x, y)   (x + y)int totalNumber = SIZE - ADD(2, 3);

rewrite 后的代码

  int totalNumber = 10 - (2 + 3);    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_d954c3_mi_0, totalNumber);

当多个宏连接使用时,运算符的级别跟连接符的级别是一样的,并不会先计算出结果然后再连接。只会一一展开

对于需要多行的宏,可以使用 \ 来连接

#define MINA(A,B)    __typeof__(A) __a = (A);\                     __typeof__(B) __b = (B);\                     __a < __b ? __a : __b;

宏的返回值

当需要把宏内容的最后一条语句的执行结果当作返回值时,我们就可以用到clang c 的扩展 ({})

#define YM_TEST(_a_, _b_)   _a_ = 100; _b_=200;    int a = 10;    int b = 20;    int res = YM_TEST(a, b);    NSLog(@"res =  %d", res);

转换后的代码

    int a = 10;    int b = 20;    int res = a = 100; b=200;;    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_602c07_mi_0, res);

输出结果 res = 100

如果想要结果为最后一个表达式的值,

#define YM_TEST(_a_, _b_)   ({_a_ = 100; _b_=200;})    int a = 10;    int b = 20;    int res = YM_TEST(a, b);    NSLog(@"res =  %d", res);

转换后的代码

    int a = 10;    int b = 20;    int res = ({a = 100; b=200;});    NSLog((NSString *)&__NSConstantStringImpl__var_folders_cg_1qk1vr1577l3c1f6bbdbt5hm0000gn_T_ViewController_f857bd_mi_0, res);

输出结果 res = 200

上面的就是为了举例而举例,实际应用可以参考一下前文提到的

#define YM_Swap(_a_, _b_) { __typeof__(_a_) _t_ = _a_; (_a_) = (_b_); (_b_) = (_t_); }

## 连接连个变量

oc中min宏的定义

#define __NSX_PASTE__(A,B) A##B#define __NSMIN_IMPL__(A,B,L) ({__typeof__(A) __NSX_PASTE__(__a,L) = (A);\__typeof__(B) __NSX_PASTE__(__b,L) = (B);\(__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__a,L) : __NSX_PASTE__(__b,L);\})#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)int c = MIN(10, 20);NSLog(@"c= %d",c);

转换后的代码

 int c = ({ int __a0 =  (10); int __b0 =  (20); (__a0 < __b0) ? __a0 : __b0; });
__COUNTER__   是一个全局的变量, 这里为0我们调用时,实际操作为 min (10, 20, 0)__typeof__(A) __NSX_PASTE__(__a,L) = (A)  转成 int __a0 = 10__a##0  的结果就是__a0,就是简单的连接。

全局的COUNTER 完全避免了名字的重复。

##__VA_ARGS__ 处理可变参数

#define DeLog(format, ...) do {                                                                          \printf("<%s : %d> %s\n",                                           \[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \__LINE__, __func__);                                                        \NSLog(format, ##__VA_ARGS__);                                           \} while (0)

在宏中使用 … 定义一个可变参数, 当需要用到这个可变参数时,就要使用 ##VA_ARGS 来解析所有的可变参数。

do {} while(0)是干什么用的, 为什么不直接写成 {}。 说实话其实没什么用,只是让转化后的代码更好看,如果没有 do…while 转换后的代码 { };。 如果有,转换后的代码 do{ } while(0); 只是花括号后面跟一个;不好看而已, 话说回来了,我们又看不到。所以就无所谓了,写成什么都行。而且编译器会把他优化掉,不会影响效率。