zoukankan      html  css  js  c++  java
  • ios宏的使用和技巧

    替换字符


    #define M_PI 3.14159265358979323846264338327950288
    

    简单函数宏


    //定义:
    #define add(a,b) a+b
    //使用:
    add(1,2) //打印3
    

    复杂函数宏


    之前的内容很容易明白了对吧,不过复杂的函数宏就没那么容易明白了。先看一下宏中的一些常用的特殊符号和系统方法

    //关键字
    ...:可变参数
     __VA_ARGS__ :宏定义中的...中的所有剩余参数
     ##:连接符号
     #:原样输出
    /:换行符
    
    //系统工具方法
    __COUNTER__ 无重复的计数器,从程序启动开始每次调用都会++,常用语宏中定义无重复的参数名称
    __FILE__:当前文件的绝对路径,常见于log__LINE__:展开该宏时在文件中的行数,常见于log__func__:所在scope的函数名称,常见于log

    复杂宏会用到 #,##,...,__VA_ARGS__等关键字和系统方法,这些关键字组合可以实现一些技巧,比如换参数和换方法名等等,多个宏结合使用,完成一些高级的功能.接下来演示的,用来获取方法参数宏(10个参数以内)

    /*
    获取方法参数的宏bb_argcount()
    它的好处不仅将计算在预处理时搞定,不拖延到运行时的cpu;更重要的是编译检查。如某些可变参数的要求2个或3个参数,其他的都不行。只有这样的宏才能在编译前就确定参数是否满足要求
    参数分类很多步骤,通过不懂的替换宏名称和参数,从而达到计算出方法参数个数的功能。
    */
    #define bb_argcount(...) bb_at(10, __VA_ARGS__,10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
    #define bb_at(N,...) bb_concat_at##N (__VA_ARGS__)
    #define bb_concat_at10(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,...) bb_head(__VA_ARGS__)
    #define bb_head(...) bb_head_first(__VA_ARGS__,0)
    #define bb_head_first(first,...) first
    
    
    //调用示例:
    int count = bb_argcount(a,b,c,d,e);
    NSLog(@" count is :%d",count);
    //输出: count is :5
    

    是不是很神奇?下来来一步步分析下具体如何实现的。

    bb_argcount(a,b,c,d,e);//假设我们传入5个参数
    

    步骤1:带入bb_argcount

    //#define bb_argcount(...) bb_at(10, __VA_ARGS__,10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
    //请注意...和__VA_ARGS__的使用,...是可变参数,传入的是a,b,c,d,e会替换__VA_ARGS__的部分,得到如下部分
    int count = bb_at(10, a, b, c, d, e, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
    

    步骤2:接下来带入bb_at

    //#define bb_at(N,...) bb_concat_at##N (__VA_ARGS__)
    //第一个参数为N,之后都是可变参数,所以N为10,__VA_ARGS__ 为 a, b, c, d, e, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
    //##是把字符连接起来,通过##我们重新使用了新的宏,这里就是bb_concat_at10
    //这一步即修改了参数,又修改了方法名
    int count = bb_concat_at10(a, b, c, d, e, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
    

    步骤3:带入bb_concat_at_10

    //bb_concat_at10(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,...) bb_head(__VA_ARGS__)
    //把前面10个参数都换了,第10位以后的参数设为可变参数
    int count = bb_head(5,4,3, 2, 1)
    

    步骤4:带入bb_head

    //bb_head(first,...) first
    //直接获取第一个数,其他的省略
    int count = 5;
    

    通过4步,就得到了想要的结果,是不是很神奇,很有意思?宏还有许多其他高级的用法,大家可以看一下ReactiveCocoa,这个库源码里的宏写的非常多,有很多很有意思的宏。

    宏的健壮性

    宏虽然定义和使用很简单,但是想用好,想把宏定义的健壮不出bug,却不是那么容易。

    #define MIN(A,B) A < B ? A : B
    

    这个宏的作用是返回小的那个数,这个宏会有什么问题?我来列出可能导致这个宏出错的几种情况

    //exp1
    int a = 2 * MIN(3, 4);
    //exp2
    int a = MIN(3, 4 < 5 ? 4 : 5);
    //exp3
    float a = 1.0f;
    float b = MIN(a++, 1.5f);
    
    

    为什么会出错?大家把参数带入到宏里面展开就知道了。详细展开我也不说了,大家看我下面参考中的那篇文章,里面有详细过程。

    来给出系统的标准定义,看,没那么容易实现吧。

    #if !defined(MIN)
        #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__)
    #endif
    

    ##如何查看宏的展开内容?

    如果大家看这些宏看起来很吃力的话可以用下面这个LOG_MACRO查看宏的内容.宏的展开顺序为先完全展开宏参数,再扫描宏展开后里面的宏,如此反复。但是,宏内容中含有#和##这两个符号的时候,参数不会被先展开。

    //打印宏展开后的函数
    #define __toString(x) __toString_0(x)
    #define __toString_0(x) #x
    #define LOG_MACRO(x) NSLog(@"%s=
    %s", #x, __toString(x))
    

    ##优缺点

    宏的优点:

    -  最主要还是帮你省点事,少写点代码。
    -  实现一些高级的功能
    

    缺点

    -  swift不支持
    -  名称没起好很容易造成歧义
    -  代码不是很易读
    -  宏没写好容易导致未知的bug
    自己选的路,跪着也要走下去......
  • 相关阅读:
    final和finally的区别
    ArrayList和LinkedList的区别
    collection和collections的区别
    第三次作业
    第二次作业
    第零次作业
    最后一次作业-- 总结报告
    第14、15教学周作业
    GridView去掉边框! 【转载于:http://magicpeng99.blog.sohu.com/】
    ASP.NET支持用Menu显示web.sitemap中定义好的网站链接 【转载】
  • 原文地址:https://www.cnblogs.com/zmc815/p/6905258.html
Copyright © 2011-2022 走看看