Objective-C 宏定义详细介绍
2016-12-02 20:00
232 查看
喜欢读一些开源项目源码的人,总是会发现,大神的代码中总是有那么一些简短而高效的宏定义,点击进去一看,发现晦涩难懂,别说学习了,有时候理解都是一种困难,但是宏定义本身并没有那么难,但是写出一个好的宏当然还是需要丰富的经验和技术,接下来就说一说宏定义,看懂大神的宏是第一步,偶尔写一个也是装逼的好办法~
定义:
宏定义分为两种:一种是对象宏(object-like macro)另一种就是函数宏(function-like macro)
根据名字也可以理解到,对象宏就是用来定义一个量,通过这个宏可以拿到这个变量,比如我们定义一个π值: #define PI 3.1415926在这里如果用到π值时,就不需要再写出一个浮点数了,而直接使用PI就相当写入了这个常量浮点数,其本质的意义在于把代码中的PI在编译阶段替换为真正的常量,一般用来定义一些常用的常量,比如屏幕的宽高、系统版本号等。但是需要注意的是,但你定义一个表达式为宏的时候,需要透过宏的表面,看到器编译的本质,例如
#define MARGIN 10 + 20
但你用它来计算一个宽度时,如果用到了MARGIN * 2,结果将会非你所愿,你得到的会是一个50而并非60,展开表达式就可以看到
MARGIN * 2 // 展开可以得到
// 10 + 20 * 2 = 50
我们需要考虑到它的运算优先级,解决的方式很简单,再它的外层加上一个小括号
#define MARGIN (10 + 20)
// MARGIN * 2
// (10 + 20) * 2 = 60
复制代码
函数宏的作用就类似于一个函数一样,它可以传递参数,通过参数进行一系列的操作,比如我们常用的计算两个数的最大值,我们可以这样来定义
#define MAX(A,B) A > B ? A : B
复制代码
这样写看起来是没有问题的,进行简单的比较MAX(1,2)发现也是没有什么问题,但是当有人使用你的宏进行更加复杂的计算时就回出现新的问题,比如进行三个数值的计较时,可能会这样写
int a = 3;
int b = 2;
int c = 1;
MAX(a, b > c ? b : c) //
= 2
复制代码
结果肯定也不是你想要的,最大值很明显是3,但是计算的结果确实2,这其中发生了什么导致计算出错,我们可以展开宏来一探究竟,下面是宏的展开
MAX(a,b > c ? b : c);
//a > b > c ? b : c ? a : b > c ? b : c
//(a > (b > c ? b : c) ? a : b) > c ? b : c // 这是运算的优先级
// 带入值可以看出
//( 3 > (2 > 1 ? 2 : 1 ) ? 3 : 2) > 1 ? 2 : 1
// (3 > 2 ? 3 : 2) > 1 ? 2 : 1
// 3 > 1 ? 2 : 1
复制代码
想必大家都看出来了问题所在,还是由于优先级的问题,所以在此谨记,反正多写两个括号也不会累着,不管会不会出现问题, 写上小括号终究是保险一些~
可是总有写奇葩的写法会出现,而且看开起来还很有道理的样子~
c = MAX(a++,b); // **我直接展开给你看就得了**
// c = a++ > b ? a++ : b
// c = 3++ > 2 ? 3++ : 2
// c = 4
// a = 5
复制代码
不管这样写的那个人是有多欠揍,但是毕竟看起来是没有任何问题的,所有我们要处理这样的情况,但是使用我们普通的小括号已经无法解决,我们需要使用赋值扩展({...})相信有朋友已经认出来了这种用法了,我们可以使用这样的方法来计算出一个对象,而不用浪费变量名,可以形成小范围的作用域来计算特殊的值
int a = ({
int b = 10;
int c = 20;
b + c;
})
// a = 30;
int b; // 继续使用b和c当变量名也没有问题
int c;
复制代码
再回到现在这个问题上,我们该如何改装这个宏来让其适应这个坑爹的写法呢
#define MAX(A,B) ({__typeof(A) __a = (A);__typeof(B) __b = (B); __a > __b ? __a : __b; })
__typeof()就是转换为相同类型的变量值,就完美的解决了这个问题,但是还有一个不怎么会发生的意外,通过上面也可以知道,我们生成了新的变量__a, __b,如何有人使用了__a,__b,就会应为变量名重复而编译错误,如果有人这样用了,你可以拿起你的键盘砸他一脸,原因当然不是__a使你的宏错误了,而是__a到底是什么意思,变量名的重要性不言而喻,除非你和看代码的人有仇,否则请使用有意义的变量名,接下来让我们看一看官方的MAX是如何实现的
定义:
宏定义分为两种:一种是对象宏(object-like macro)另一种就是函数宏(function-like macro)
根据名字也可以理解到,对象宏就是用来定义一个量,通过这个宏可以拿到这个变量,比如我们定义一个π值: #define PI 3.1415926在这里如果用到π值时,就不需要再写出一个浮点数了,而直接使用PI就相当写入了这个常量浮点数,其本质的意义在于把代码中的PI在编译阶段替换为真正的常量,一般用来定义一些常用的常量,比如屏幕的宽高、系统版本号等。但是需要注意的是,但你定义一个表达式为宏的时候,需要透过宏的表面,看到器编译的本质,例如
#define MARGIN 10 + 20
但你用它来计算一个宽度时,如果用到了MARGIN * 2,结果将会非你所愿,你得到的会是一个50而并非60,展开表达式就可以看到
MARGIN * 2 // 展开可以得到
// 10 + 20 * 2 = 50
我们需要考虑到它的运算优先级,解决的方式很简单,再它的外层加上一个小括号
#define MARGIN (10 + 20)
// MARGIN * 2
// (10 + 20) * 2 = 60
复制代码
函数宏的作用就类似于一个函数一样,它可以传递参数,通过参数进行一系列的操作,比如我们常用的计算两个数的最大值,我们可以这样来定义
#define MAX(A,B) A > B ? A : B
复制代码
这样写看起来是没有问题的,进行简单的比较MAX(1,2)发现也是没有什么问题,但是当有人使用你的宏进行更加复杂的计算时就回出现新的问题,比如进行三个数值的计较时,可能会这样写
int a = 3;
int b = 2;
int c = 1;
MAX(a, b > c ? b : c) //
= 2
复制代码
结果肯定也不是你想要的,最大值很明显是3,但是计算的结果确实2,这其中发生了什么导致计算出错,我们可以展开宏来一探究竟,下面是宏的展开
MAX(a,b > c ? b : c);
//a > b > c ? b : c ? a : b > c ? b : c
//(a > (b > c ? b : c) ? a : b) > c ? b : c // 这是运算的优先级
// 带入值可以看出
//( 3 > (2 > 1 ? 2 : 1 ) ? 3 : 2) > 1 ? 2 : 1
// (3 > 2 ? 3 : 2) > 1 ? 2 : 1
// 3 > 1 ? 2 : 1
复制代码
想必大家都看出来了问题所在,还是由于优先级的问题,所以在此谨记,反正多写两个括号也不会累着,不管会不会出现问题, 写上小括号终究是保险一些~
可是总有写奇葩的写法会出现,而且看开起来还很有道理的样子~
c = MAX(a++,b); // **我直接展开给你看就得了**
// c = a++ > b ? a++ : b
// c = 3++ > 2 ? 3++ : 2
// c = 4
// a = 5
复制代码
不管这样写的那个人是有多欠揍,但是毕竟看起来是没有任何问题的,所有我们要处理这样的情况,但是使用我们普通的小括号已经无法解决,我们需要使用赋值扩展({...})相信有朋友已经认出来了这种用法了,我们可以使用这样的方法来计算出一个对象,而不用浪费变量名,可以形成小范围的作用域来计算特殊的值
int a = ({
int b = 10;
int c = 20;
b + c;
})
// a = 30;
int b; // 继续使用b和c当变量名也没有问题
int c;
复制代码
再回到现在这个问题上,我们该如何改装这个宏来让其适应这个坑爹的写法呢
#define MAX(A,B) ({__typeof(A) __a = (A);__typeof(B) __b = (B); __a > __b ? __a : __b; })
__typeof()就是转换为相同类型的变量值,就完美的解决了这个问题,但是还有一个不怎么会发生的意外,通过上面也可以知道,我们生成了新的变量__a, __b,如何有人使用了__a,__b,就会应为变量名重复而编译错误,如果有人这样用了,你可以拿起你的键盘砸他一脸,原因当然不是__a使你的宏错误了,而是__a到底是什么意思,变量名的重要性不言而喻,除非你和看代码的人有仇,否则请使用有意义的变量名,接下来让我们看一看官方的MAX是如何实现的
相关文章推荐
- READING NOTE: Speed/accuracy trade-offs for modern convolutional object detectors
- mybatis返回list很智能很简答的,只需要配置resultmap进行类型转换,你dao方法直接写返回值list<对应的object>就行了啊
- TypeError: 'str' object is not callable
- eval()解析object出现Unexpected identifier异常
- ObjectMapper处理从远程获取的Object对象
- android.widget.LinearLayout.addView(android.view.View)' on a null object reference
- objective-c多种排序算法图形化演示
- Swift 微信SDK中WXApi.h 和WXApiObject.h有关报错修改方法
- Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法
- Object类
- The ServiceClass object does not implement the required method in the following form: OMElement sayHello(OMElement e)
- Notice: Trying to get property of non-object problem(PHP)解决办法 中间件只能跳转不能返任何数据
- Object-c 中截取包含(中、英文、表情符号emojiicon)混合字符串的每个字符并获取此字符的unicode值
- json 转 object
- java 对象流的使用示例-- ObjectOutputStream
- JSONObject 的简单使用
- 使用ObjectDataProvider 将函数应用到绑定
- objective-c的单例模式测试
- mybatis bug之resultmap缺少object-relation匹配参数password,造成设置密码不成功
- JSONObject与JSONArray的使用