iOS 预处理语句

知识补充

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

  预处理程序实际上是在分析程序前先处理的语句,它可以识别散步在程序中的特定语句。所有的预处理语句都使用井号(#)开头,这个符号必须是一行中的第一个非空字符。
  
  预处理语句可以大概划分成三类:文件包含宏定义条件编译,下面,就一一来讲解。


文件包含

  文件包含指的是在当前文件中用到其他文件中的函数或方法或者是其他信息时,可以将其他文件的头文件包含进来,然后再当前文件中使用,文件包含一般放到文件的开头位置。
  
  如果使用C语言编程,文件包含是#include<> 或者 #include“”。如果使用Objective-C语言,文件包含为#import<>或者#import“”。#include与#import最大的区别是#import在导入文件的时候进行了去重复检查。此外,“”和<>两个也是有区别的,“”是用来放自己写的文件,<>用来放系统文件。程序在执行的时候,会根据你写的样式,优先去寻找对应类型的文件。比如<>,会先去找系统的文件,如果找不到,再去找自定义的文件。所以,正确的选择样式,能够提高程序的运行效率。
  
  在使用文件包含的时候,会遇到A文件中用到B文件,B文件中用到A文件,这种相互使用包含的关系,这种情况就有点像死循环了,要使用A文件,必须要先有B,可是在B中,又需要先有A,因此在运行的时候,会出现错误。解决这个问题最好的办法,是用@class代替文件包含,@class就是表明有这个类,等在源文件中真正用到的时候再包含文件。

//  A.h
#import <Foundation/Foundation.h>
@class B;
@interface A : NSObject
@property (nonatomic, strong) B *obj;
@end

//  B.h
#import <Foundation/Foundation.h>
@class A;
@interface B : NSObject
@property (nonatomic, strong) A *obj;
@end

宏定义

  在程序中,有一些常量或者简短的函数是会多次重复使用的,对于这些常用的数据,我们可以使用宏定义。使用宏定义可以快速的完成程序中多处的配置,最大的好处是只要修改宏定义的值,所有使用宏定义的值都会发生改变。此外,宏定义是在程序编译之前进行替换和设置,比定义成全局变量或函数的效率要高。
  
  宏定义是通过#define来实现的,一般写在程序的文件包含的下面。宏名通常用全部的大写字母表示。下面,就通过代码举来看一下宏定义的使用。

//  main.m
#import <Foundation/Foundation.h>
#define JR_SQUARE_1(n) n*n  //求数字的平方
#define JR_SQUARE_2(n) (n)*(n) //求数字的平方
#define JR_HELLO @"hello world";
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int num2 = JR_SQUARE_1(2);
        NSLog(@"2的平方 = %i",num2);
        //结果:2的平方 = 4

        int num3 = JR_SQUARE_1(2+1);
        NSLog(@"(2+1)的平方 = %i",num3);
        //结果:(2+1)的平方 = 5

        int num4 = JR_SQUARE_2(2+1);
        NSLog(@"(2+1)的平方 = %i",num4);
        //结果:(2+1)的平方 = 9
    }
    return 0;
}

  在代码中可以看到,同样都是求一个数的平方,但是两个宏定义得到的结果却是不一样的。第一个计算2+1的平方的时候是2+1*2+1,所以结果为5,答案错误。因此,在写宏定义,带参数的时候,需要设置小括号,保证正确性。


条件编译

  条件编译其实就在编译之前由预处理器来根据预处理语句进行判断,如果满足条件,就编译满足条件下面的代码段,如果不满足条件,下面的代码段就不进入编译环节。
  
  条件编译主要分为两种,一种是判断是否定义过某个宏,根据是否定义过这个宏,来决定是否编译某段代码。另外,还有一组语句和条件结构中的阶梯if结构非常类似,但是写法上有所区别,是#if、#elif、#else、#endif组成。需要注意的是,无论哪种,都要有#endif结束标志。此外,最重要的一点是,条件编译中的条件不能使用普通的变量,一般会选择使用宏定义。

//  main.m
#import <Foundation/Foundation.h>
#define LL_COUNT 10
int main(int argc, const char * argv[]) {
    @autoreleasepool {
#ifdef LL_COUNT
    NSLog(@"定义了 COUNT 这个宏");
#endif

#ifndef LL_MAX
    NSLog(@"没有定义 MAX 这个宏");
#endif

#if LL_COUNT==1
    NSLog(@"LL_COUNT=1");
#elif LL_COUNT==2
    NSLog(@"LL_COUNT=2");
#elif LL_COUNT==3
    NSLog(@"LL_COUNT=3");
#else
    NSLog(@"LL_COUNT=%i",LL_COUNT);
#endif   
    }
    return 0;
}

注:在不满足条件下,输入的英文是白色的,并且不能触发联想


原文地址

IOS 预处理语句

相关阅读

iOS开发系列–C语言之预处理

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值