Xcode 中关于#的小知识
在代码中使用Autolayout时,大家都会使用NSDictionaryOfVariableBindings
这个宏,这个宏可以生成一个变量名到变量值映射的Dictionary。比如NSDictionaryOfVariableBindings(button1, button2)
将会生成一个{ @"button1" = button1, @"button2 = button2 }
的Dictionary。它是怎么做到的呢?我们来看看这个宏的定义:
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
这个宏定义中有3个参数,后两个参数不难理解,但第一个参数中间有个#
符号,语法上看起来比较怪异,这个是什么呢?以前在做越狱的mobilesubstrate开发时,其中定义的一堆宏频繁使用了这个符号,下面就来揭开#
这个符号在宏定义中的迷雾。
预编译的一些知识
我们的代码在build时并不是直接进行编译的,在编译之前还进行了预编译处理。预编译会把include或import的文件导入到文件中,同时会将代码中用到的宏进行替换。注意宏是直接在代码中替换成宏的定义的,如果有嵌套也会逐层替换。
“#”指示一些预编译命令
预编译命令一般都是以#
开头的,比如#include
、#import
、#if
等,在这里就不一一说明了,本文主要说明一下#
在宏定义里面的一些作用。
宏参数字符串化
在一个参数前加一个#
,预处理时将会变成这个参数名的字符串常量,即字符串化(stringify)。比如:
#define GET_NAME(X) #X
int a = 0;
NSLog(@"%s",GET_NAME(a)); //output: "a"
NSLog(@"%s",GET_NAME(a+3)); //output: "a+3"
将会得到以下输出:
a
a+3
可以看出#
,将参数原样转换成字符串常量,如果参数是一个表达式,那么输出这个表达式的原样字符串常量。
回头再看看NSDictionaryOfVariableBindings
的定义:
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
如果这样生成两个button的映射:
NSDictionaryOfVariableBindings(button1, button2);
那么预编译时就会转换成:
_NSDictionaryOfVariableBindings(@"""button1, button2", button1, button2, nil);
由于两个常量字符串放在一起就是字符串常量串联,将变成两个字符串常量组合在一起的字符串常量,也就是上面是一个空字符串""
和"button1, button2"
串联,所以上面的代码等价于:
_NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);
那么_NSDictionaryOfVariableBindings
函数就可以将它的第一个参数按逗号,
分割开作为key,后面就是各个key对应的值了。因此这段代码就创建了一个内容为{ @"button1" = button1, @"button2 = button2 }
的Dictionary。
命名的串联
#
在宏定义中的另一个作用就是用于命名的串联,用##
就可以串联它左右两边的命名,比如以下代码:
#define CONCAT(X, Y) X ## Y
NSString *helloworld = @"Hello, world!";
NSLog(@"%@",CONCAT(hello, world)); //output: "Hello, world"
CONCAT(hello, world)
实际被转换成helloworld
。注意一下,因为宏是预编译阶段进行展开的,就是说在编译之前,因此代码中的hello
和world
即使没有定义其实也是没问题的,预编译处理后,这两个命名是不存在的。
可选可变参数
##
在宏定义中可以放在__VA_ARGS__
之前表示可变参数可以为空,否则的话可变参数至少为一个了。
#define MYLOG(format, ...) NSLog(format, ##__VA_ARGS__)
MYLOG(@"Don't make an error!");
上面代码中MLOG中只有一个参数,如果不加##
,则MLOG至少需要两个参数,在Xcode里将会出现编译错误。
转自:http://blog.xcodev.com/blog/2013/12/16/mists-of-the-sharp/
转载于:https://www.cnblogs.com/yingkong1987/p/3463529.html
Xcode 中关于#的小知识相关推荐
- c语言冷门小知识,生活中的冷门小知识有哪些
其实生活中充满了冷门知识,比如喝醋能够防止晕车.花生可以祛牙黄等等.下面是学习啦小编为大家整理的关于生活中的冷门小知识,希望大家喜欢! 生活中的冷门小知识 洗衣机强档比弱档节能 很多人没注意,在同样长 ...
- Python中的一些小知识
1.时间处理 import time # time.struct_time结构体 time1 = time.localtime() print(time1) # 从1970年到现在的秒数 time2 ...
- ubuntu server中的一些小知识(个人收集贴)
安装apache, ssh apt-get install apache2 a2enmod apt-get install openssh-server Bash的导航命令 大多数人都知道Bash的导 ...
- Javascript中的一些小知识收集
1.在地址栏中传递参数时,对于包含特殊字符的处理: 使用encodeURIComponent()对参数值进行编码处理就可以了. 转载于:https://www.cnblogs.com/lfzwenzh ...
- vue 中indexof_前端小知识-Vue中使用indexOf() 方法
定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置. 语法 stringObject.indexOf(searchvalue,fromindex) 参数描述searc ...
- CSS中的背景小知识
1.css中的背景有很多属性,在写的时候可以用background:xxx xxx 写在一起:也可以分开写,如background-color:background-image等等 2.背景颜色的写 ...
- Java匿名内部类中this的小知识(待补充完整)
今日在看到Enumeration 借口的源码时发现,Enumeration 接口的asIterator 方法返回了一个迭代器,并使用匿名内部类(实现接口的匿名内部类).对迭代器中的方法进行了实现 de ...
- MSCKF中零碎的小知识
当把MSCKF掰碎了一点一点细品时,发现这里面的东西确实很多,也很经典,滤波的多传感器融合绝对值得去细细研究. 1.关于ros的时间戳问题 roscpp 里有两种时间表示方法:时刻 (ros::Tim ...
- 在日常办公中如何正确使用计算机,日常办公中的节能降耗小知识
节能降耗,从我做起,从点滴做起,从举手之劳做起,今天就让我们来分享下日常办公中节能的小知识吧~ 推广使用节能灯 使用电子镇流器代替电感式镇流器.以节能灯(T5T8)代替白炽灯,可节省高达70%至80% ...
最新文章
- 甲骨文推出低成本高速公共与混合云方案,矛头直指AWS
- python--内置函数
- 用python控制钉钉软件_Python—实现钉钉后台开发
- json 字符串反序列化成DataSet
- Python跨文件引用模块
- 树莓派4b自带wifi_树莓派4B日志二:WiFi远程配置与换源
- C++socket编程(七):7.3 http的响应协议
- 手把手带你深入解析静态分派 动态分派原理 | 原力计划
- 虚拟机中试用windows 8(视频)
- mac删除的文件还能找回吗
- 终极.NET混淆器丨.NET Reactor产品介绍
- 小程序源码:全新独立后台修复登录在线答题
- C#实现改变图片的像素
- 阿里云计算工程师ACP考题归类解析
- 2021.11.08【web刷题记录】
- win7忘记密码怎么办,Administrator忘记密码解决办法【完美、简单、有效】
- Androidstudio报错问题之R飘红
- JS实现点击导航栏鼠标左右滑动 (适用于PC端和移动端)
- 【转】腾讯 百度 网易游戏 华为Offer及笔经面经
- 一文带你了解优秀的Go 语言开源项目