• 【iOS】预处理器


       转载请注明出处!!!

    之前一直没有系统研究过预处理器这一东西,今天在读《精通Objective-C》这本书时,里面有一章讲到了预处理器。今天就好好研究一下。

    乍一听预处理器,我们不明觉厉,感觉我们没有用过这个东西,其实我们在开发中就用过很多次,只是我们不知道而已。我举几个例子就像#import "Elements.h"就是一个预处理器指令,还有一些宏定义也是宏指令。这样一想我们在开发中也是经常用到预处理器的。

    众所周知,程序中的源代码算计无法识别,需要将写好的代码转成0、1二进制代码,计算机才能识别。将源代码转成二进制代码需要两步:编译和链接。编译是通过编译器将每个文件的代码都转为二进制代码,在这个过程中,如果有语法错误,会有编译失败的提示,如果成功,那么会生成对应多个目标文件。在一个文件中可能会到其他文件,因此,还需要将编译生成的目标文件和系统提供的文件组合到一起,这个过程就是链接。经过链接,最后生成可执行文件。通常人们所理解的程序运行就是编译和链接两个阶段,但实际上在编译之前预处理器要进行预处理操作,处理完成之后才进入到编译阶段。因为预处理指令是在编译之前就进行了,所以它比程序运行时进行操作的效率高。

    Objective-C编译器编译源代码的一般流程是:接受源文件,然后把它们转换为能够在目标平台上执行的文件。

    预处理器是在语法分析阶段前的语法分析阶段发挥作用的。

    预处理:分析程序前先处理的语句,它可以识别散布在程序中的特定语句。所有的预处理语句都适用“#”开头,这个符号必须是一行中的第一个非空字符。

    预处理语言对原文件进行的转换主要包括源文件的内容、条件编译和宏展开。大致可以分为三类:文件包含、宏定义和条件编译。

    1.头文件包含(#include、#import)

    作用是是预处理器获得被包含文件的文本,并将其插入到当前文件中。一般放在文件的开头。

    #include#import最大的区别是** #import 在导入文件的时候进行了去重复检查,此外,""<>两也是有区别的,""一般是用来引用自定义的文件,<>一般是用来引用系统的文件。程序在执行的时候,会根据你写的样式,优先去寻找对应类型的文件。比如<>**会先去找系统文件,如果找不到,再去找自定义文件。所以正确的选择样式,能够提高程序的执行效率。
     
    循环引用:在使用文件包含的时候,会遇到A文件中用到B文件,B文件中用到A文件,这种互相使用包含的关系就有点类似死循环了,运行的时候就是报错。解决这个问题最好的办法就是用@class代替文件包含,@class就是表明有这个类,等在源文件中真正用到的时候才会去包含文件。
    #import <Foundation/Foundation.h>
    @class B;
    @interface A : NSObject
    
    @property (nonatomic, strong) B *obj;
    
    @end
    
    #import <Foundation/Foundation.h>
    @class A;
    @interface B : NSObject
    
    @property (nonatomic, strong) A *obj;
    
    @end

    2.条件编译(#if、#elif、#else、#endif、#ifdef和#ifndef)

    #if指令可以测试表达式的值,并根据结果确定是否包含部分源文件 与#endif指令配对使用

    条件编译主要分为两种:一种是判断是否定义过某个宏,根据是否定义过这个宏,来决定是否编译某段代码。另外,还有一组语句和条件结构中的阶梯if结构非常类似,但是写法上有区别,是#if、#elif、#else、#endif组成。需要注意的是,无论哪种,都要有#endif 结束标志。此外,最重要的一点是:条件编译中的条件不能使用普通变量,一般会选择使用宏定义。
    #import <Foundation/Foundation.h>
    
    #define JR_COUNT 10
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
    #if defined(JR_COUNT)
            NSLog(@"定义了 COUNT 这个宏");
    #endif
            
    #if defined(JR_MAX)
            NSLog(@"没有定义了 JR_MAX 这个宏");
    #endif
            
    #if JR_COUNT==1
            NSLog(@"JR_COUNT=1");
    #elif JR_COUNT==2
            NSLog(@"JR_COUNT=2");
    #elif JR_COUNT==3
            NSLog(@"JR_COUNT=3");
    #else 
            NSLog(@"JR_COUNT=%i",JR_COUNT);
    #endif
            
        }
        return 0;
    }

    3.宏是指有名称的代码段。使用#define 定义宏使用#undef移除宏

    #import <Foundation/Foundation.h>
    
    #define JR_PI 3.14
    
    #define JR_MAX(a,b) ((a>b)?(a):(b)) //得到两个数中较大值
    
    #define JR_SQUARE_1(n) n*n  //求数字的平方
    #define JR_SQUARE_2(n) (n)*(n) //求数字的平方
    
    #define JR_HELLO @"hello world";
    /*
    同样都是求一个数的平方,但是两个宏定义得到的结果却是不一样的,第一个计算2+1的平方的时候是2+1*2+1,所以结果为5,答案错误。因此在写宏定义的时候,带参数需要设置小括号,确保正确性。
    */

    4.其他

    诊断(#error、#warning和#line)

    #pragma指令

    最后补充下常用指令

    #    空指令,没有任何效果
    #include 包含一个源代码文件
    #define 定义宏
    #undef 取消定义宏
    #if 如果条件为真,则编译下面的代码
    #elif 如果前面的#if不为真,则编译下面的代码
    #endif 结束一个#if...#elif条件编译块
    #ifdef 如果已经定义了某个宏,则编译下面的代码
    #ifndef 如果没有定义某个宏,则编译下面的代码
    #error 停止编译并显示错误信息
    #pragma mark -  名   
  • 相关阅读:
    Haproxy的安装与配置
    keepalived工作原理和配置说明
    服务器集群与负载均衡基础知识
    Linux磁盘分区与格式化
    第12章 在.NET中操作XML
    第16章 多线程
    第10章 网络编程
    第8章 流和序列化
    关于引用类型作为参数加上ref与不加ref的区别
    第3章 C#中的委托和事件
  • 原文地址:https://www.cnblogs.com/weicyNo-1/p/8528483.html
Copyright © 2020-2023  润新知