iOS预编译指令详解

作者: 寻形觅影 | 来源:发表于2016-11-29 16:21 被阅读864次

    1.预处理的概念

    ANSI C标准(ANSI C是美国国家标准协会(ANSI)对C语言发布的标准)规定可以在C源程序中加入一些“预处理命令” ,以改进程序设计环境,提高编程效率。这些预处理命令是由ANSI C统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译,因为编译程序不能识别它们,必须在对程序进行通常的编译之前,先对程序中这些特殊的命令进行处理,这一过程就是我们说的“预处理”。经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译,得到可供执行的目标代码。

    2.预处理指令功能

    在C语言中包括三类预处理指令:1.宏定义 2.条件编译 3.文件包含。

    宏定义:

    (1)宏的定义

    定义格式: #define 标识符 字符串
    1、 不含参数: #define TAG_VIEW 10086
    2、 含有参数:#define MAX_INT(a,b) a+b

    (2)宏定义的说明

    1、宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。
    2、 宏定义是用宏名代替一个字符串,只作简单置换,不作正确性检查,同时也不会做运算逻辑处理,同时在进行宏定义时,可以引用已定义的宏名,可以层层置换。(在这里需要特别注意的是:当宏涉运算时,要根据情况来添加括号,防止运算逻辑出现错误,如下图所示,当你宏MAX_TXT想作为整体来使用时应该写成这样: #define MAX_INT (2+3) 这样MAX_END的结果应该是25,如果不加括号,只会进行置换行为,MAX_END的结果就为11。在这非常感谢 @星星月亮落一地 的提醒)


    3、 宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。
    4、 #define 命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本文件结束。通常,#define 命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。
    5、 可以用
    #undef
    命令终止宏定义的作用域。

    6、对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。NSLog(@"MAX_INT = %d", 10086)
    7、宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。

    条件编译:

    条件编译就是在编译之前预处理器根据预处理指令判断对应的条件,如果条件满足就将对应的代码编译进去,否则代码就根本不进入编译环节(相当于根本就没有这段代码)。

    常用条件编译函数

    1、#if 编译预处理中的条件命令, 相当于C语法中的if语句
    2、#ifdef 判断某个宏是否被定义, 若已定义, 执行随后的语句
    3、#ifndef 与#ifdef相反, 判断某个宏是否未被定义
    4、#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
    6、#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
    7、#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
    8、#if 与 #ifdef 的区别:#if是判断后面的条件语句是否成立,#ifdef是判断某个宏是否被定义过。要区分开!

    #ifdef  MAX_F
    // 如果定义了宏MAX_F,则编译此处的代码
    #else
    // 如果没有定义宏MAX_F,则编译此处的代码
    #endif
    
    // 同样
    #ifndef  MAX_F
    // 如果没有定义宏MAX_F,则编译此处的代码
    #elif MAX_INT
    // 如果定义了宏MAX_F,同时还定义了宏MAX_INT,则编译此处的代码
    #else
    // 定义了宏MAX_F,但是没有定义宏MAX_INT,则编译此处的代码
    #endif
    

    另外,在创建一个头文件或pch文件 --- 单独的一个.h文件时,常看到文件内自带了下述格式内容

    #ifndef Header_h 
    #define Header_h 
    
    #endif
    

    上面的格式是为了防止该头文件被引用时发生重复引用。

    文件包含:

    C语言下一般使用 #include, OC中一般使用#import ,它们的区别是:在使用#include的时候要注意处理重复引用,#import大部分功能和#include是一样的,但是他处理了重复引用的问题,我们在引用文件的时候不用再去自己进行重复引用处理。OC中还有一个引用声明 @class主要是用于声明一个类,告诉编译器它后面的名字是一个类的名字,而这个类的定义实现是暂时不用知道的。一般来说,在interface中(.h文件)引用一个类,就用@class,它会把这个类作为一个类型来使用,而在实现这个interface的文件中,如果需要引用这个类的实体变量或者方法之类的,还是需要import这个在@class中声明的类。

    3.关于宏的灵活使用和拓展:

    1、在宏中拼接新的宏(限字符串)

    (1)OC的代码写法:

    #define aaa @"i am aaa"
    #define bbb @"i am bbb"
    #define  ccc @""bbb@" "aaa@""
    

    打印ccc的结果如下


    (2)C的写法

    #define aaa "i am aaa"
    #define bbb "i am bbb"
    #define  ccc ""bbb" "aaa""
    

    打印结果是一样的。

    2、预处理连接符:#操作符的字符串化和 ##操作符

    (1)在含参数宏中,假如希望在字符串中包含宏的参数本身,#符号用作一个预处理运算符,它可以把语言符号转化成字符串该过程称为字符串化(stringizing)。

    //定义含参宏
    #define MAX_TAG(maxTag) NSLog(@"the max tag "#maxTag" %d", maxTag *3)
    -----------------------------------------------------------------------
    // 调用
     MAX_TAG(33);
    -----------------------------------------------------------------------
    //打印结果为
    2016-11-29 10:53:45.655 SpeachTest[2294:271425] the max tag 33 99
    

    (2)# #操作符结合了两种标记成一个token,该标记可以替代部分字符串。

     #define MMM(a)   a ## Button
    --------------------------------------------------------------------
        NSString * MMM(title) = @"asd";
        NSLog(@"titleButton = %@",MMM(title));
        NSLog(@"titleButton = %@", titleButton);
    --------------------------------------------------------------------
    //打印的结果
    2016-11-29 10:57:27.364 SpeachTest[2179:233773] titleButton = asd
    2016-11-29 10:57:27.367 SpeachTest[2179:233773] titleButton = asd
    

    在这里我并没有直接定义“titleButton”,但是打印titleButton不但不报错而且titleButton已经定义过了。 titleButton其实就是MMM(title),这就是##的作用,大家可以试一下。

    3、在宏定义中换行

    只需要在每行的结尾添加反斜杠“\”

    #define SHOWALERT(m)\
    {\
        UIAlertView *alert  =  [[UIAlertView alloc]  initWithTitle:@" 提 示 "  message:m delegate:nil cancelButtonTitle:@"确认" otherButtonTitles:nil];\
        [alert show];\
    }\
    

    4、 defined()函数 与 #if等条件条件编译函数的应用

    #define aaa "i am aaa"
    #if ( defined(aaa) && !defined(bbb) )
    #define bbb "i am bbb"
    #else
    #define bbb "i am bbb_2"
    #endif
    

    打印bbb的结果:


    5、 省略号···和 VA_ARGS

    关于省略号··· 代表的是个数可变的参数即可变参数,一般会与__VA__ARGS__结合使用,__VA__ARGS__表示保留名,将省略号省略的参数传递给宏。

    // 例如我们最常见的形式
    #ifdef DEBUG
    #define JRLog(...) NSLog(__VA_ARGS__)
    #else
    #define JRLog(...)// 如果编译经过在这里那么JRLog(···)无意义
    #endif
    

    还有当含参数宏的参数至少有一个时:

    #ifdef DEBUG
    #define JRLog(aaa, ...) NSLog(aaa, ##__VA_ARGS__)
    #else
    #define JRLog(...)
    #endif
    

    此时##的作用是在可变参数被忽略或为空的情况下,‘##’操作将使预处理器去除掉“···”前面的那个逗号。

    • 特别注意:省略号···只能出现在参数的最后面,放在其他参数中间或者前面是不可以的!

    6、iOS常用的系统内参数宏

    // 判断是否是真机
    #if TARGET_OS_IPHONE // 在这里一定不能使用#ifdef,因为TARGET_OS_IPHONE无论在真机还是模拟器情况下都存在只不过 模拟器时值为0
    #else
    #endif
    // 判断是否是模拟器
    #if TARGET_OS_SIMULATOR // 同上。"TARGET_IPHONE_SIMULATOR"已经废弃
    #else
    #endif
    // 判断手机系统版本
    #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
    #else
    #endif
    // 规定只能在ios系统下运行
    #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 
        // 规定运行支持的最小版本
        #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
        #else
        #endif
    #endif
    // 可以参照Availability.h 文件  文件路径 /Applications/Xcode8.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include 不同Xcode版本路径略有差异
    /**
     #define __IPHONE_2_0      20000
     #define __IPHONE_2_1      20100
     #define __IPHONE_2_2      20200
     #define __IPHONE_3_0      30000
     #define __IPHONE_3_1      30100
     #define __IPHONE_3_2      30200
     #define __IPHONE_4_0      40000
     #define __IPHONE_4_1      40100
     #define __IPHONE_4_2      40200
     #define __IPHONE_4_3      40300
     #define __IPHONE_5_0      50000
     #define __IPHONE_5_1      50100
     #define __IPHONE_6_0      60000
     #define __IPHONE_6_1      60100
     #define __IPHONE_7_0      70000
     #define __IPHONE_7_1      70100
     #define __IPHONE_8_0      80000
     #define __IPHONE_8_1      80100
     #define __IPHONE_8_2      80200
     #define __IPHONE_8_3      80300
     #define __IPHONE_8_4      80400
     #define __IPHONE_9_0      90000
     #define __IPHONE_9_1      90100
     #define __IPHONE_9_2      90200
     #define __IPHONE_9_3      90300
     #define __IPHONE_10_0    100000
     #define __IPHONE_10_1    100100
     */
    

    开发常用的一些宏定义网上有很多,这里就不一一列举了,希望对读者有帮助。

    相关文章

      网友评论

      • 貓秋:请问下,如何动态修改预编译文件的内容啊??比如说动态修改pch里的url地址?谢谢
      • 你看见我的小象了吗:看见开头的时候就发现不是问题的问题。是不是宏定义的时候涉及到运算的能加括号就加括号呢?不加括号真的容易出现问题,一个不算老司机的老司机给您的建议。
        你看见我的小象了吗:@寻觅_小邪 更新一下吧。别让读者误解了。因为之前是做嵌入式的对于宏定义啊什么的吃过很多的亏,就记下来了。
        寻形觅影:@星星月亮落一地 谢谢♪(๑ᴖ◡ᴖ๑)♪,是的,在涉及运算时要根据情况添加括号的使用,因为涉及到了运算符号的优先级问题,有时不加括号非常容易出问题,就好像我上面举得2+3那个例子一样

      本文标题:iOS预编译指令详解

      本文链接:https://www.haomeiwen.com/subject/ohkopttx.html