注明:本篇blog是参考多篇blog后的一个整理汇总,另外加上了自己常用的一些宏定义。如有冒犯,请会知,将删除该部分。
求最小值
宏在OC中使用是很多的,官方也定义了大量的宏。宏简单来讲就是替换。但是这个替换里面稍不注意还是很多坑的。比如举一个获取最小值的宏。
|
|
这样写你就惨了,比如遇到下面的情况:
|
|
具体自行将宏替换后就知道发生了什么,所以出了下面的版本。
|
|
这个版本你依然继续崩溃吧,比如遇到下面的例子
|
|
好,再来
|
|
看似应该完美了,但,总有刁民想害朕!如下:
|
|
此时应该有翻桌子的图。
为了解决这个问题,需要用到GNU C的赋值扩展,即({}),会将括号里面的最后一个表达式的赋值返回。例子如下:
|
|
然后就可以解决刚刚的min问题
|
|
最后来看看clang中的MIN的写法,如下
|
|
你会发现这里是一大堆。先来解释一下,首先看第一个宏
|
|
“##”的意思是将A和B连接起来。因为宏中的参数都是有意义的,要把AB连起来不能直接写成AB。有时候你还会看到这样的语句:##A,也就是##前没有变量。这里需要提到##的另外一个特性,就是##后面的变量A为空的时候,那么它会将前面一个逗号吃掉。这里还有一个单井号#顺便提一下,这个的意思是将后面的参数字符串话,也就是替换之后在这个参数左右加上””,另外由于OC中的字符串是需要加@的。所以一般会看到写成这样@#expression。
现在我们明白了,这个宏就是一个拼接函数。
再来看最后一个宏
|
COUNTER是一个预定义的宏,这个值在编译过程中将从0开始计数,每次被调用时加1。因为唯一性,所以很多时 候被用来构造独立的变量名称。这里的作用在这里(L就是COUNTER),也就是将定义的参数名字后面加了一个常数。
|
|
这确保了定义的参数名是会变的。有博客说这里是为了避免同一scope里面重复出现a,怕的是使用宏的语句之前有定义了a。但我手动测试发现(XCode8),即使在前面定义了a,在宏的语句里面也不会出现问题。debug里面可以发现同时存在了两个变量a。我猜测是({})将宏限定在了一个scope里面。根据{}会屏蔽外部同名变量的原则,即使在宏里面定义同名变量也没问题。
好了,MIN的宏就到此为止。
while?
如果有经常看宏的同学应该会发现这样的写法:
|
|
先解释一下宏参数列表里面的…,这三个点代表的是可变参数,就是可以任意个,包括0个。然后你可以用VA_ARGS来代表这一堆的可变参数。当然你还可以写成 name… ,这样的话,需要使用name来代替可变参数。
然后再来看这个写成while(0)的do…while是做什么用。
考虑一下下面的语句:
|
|
假如没有do…while,懂了吧,宏展开之后,if对应的执行语句就只有一行。如果if后面还有else的话,那么直接就报错了。
有人说,那么用括号吧宏括起来就好了吧,确实可以,但是宏后面那个分号,如果在有else的情况下,会成了下面的样子。
|
|
看到else前面那个分号没,此时应该有捂脸哭的表情。于是有了do…while的写法,这个语句可以把那个悲催的分号吃掉。这样就可以把这个宏当成一句语句来使用了。(此时想起了swift可以不用分号的好处,这不知能节省多少劳动力,但swift没宏,捂脸哭)
宏和常量的区别:
宏只是简单的替换,如果是字符串的话,建议使用常量定义,这样只有编译一次。
常量字符串定义:
在.h文件中
|
|
在.m文件中
|
|
常用宏
最后举例一些常用宏:
1、获取屏幕尺寸,横屏竖屏都可以。
|
2、状态栏和导航栏的高度
|
3、颜色
|
|
4、设备类型
|
|
5、iOS版本
|
6、弱引用宏,这个用得很多。这里很巧妙地用autoreleasepool和try来加了一个@。注意着两对宏要同时使用,在block外面用@weakify(A)后,在block里面使用@strongify(A)。
|
7、单例宏,再也不用写一堆代码了
|
|
8、懒加载宏,只针对能简单init的类。至于为啥不在这里addSubview,见我另外一篇博客《iOS懒加载的坑》。
|
|
9、获取沙盒路径
|
|
10、Log。Release版本不编译打印语句,而且打印语句有类、方法、文件的第几行信息。
|
RAC里面还有一个能判断参数有多少个的宏,可以参考这篇博客
http://www.cocoachina.com/ios/20140621/8905.html