Objective-C学习总结#2——预处理

来源:互联网 发布:免费的crm软件 编辑:程序博客网 时间:2024/06/01 08:54

Objective-C学习总结#2——预处理

预处理是Objective-C中的特殊命令,这也是OC的C语言特性。在编译器对程序进行编译之前,编译器会对这些预处理命令进行处理,然后讲这些预处理的结果与源程序一起进行编译。

预处理命令有如下两个特征:

  1. 预处理命令都必须以#开头。
  2. 预处理命令通常位于程序开头部分。

  • Objective-C学习总结2预处理
    • 宏定义
    • 带参数的宏定义
    • 条件编译
    • include与import


宏定义

使用#define、和#undef执行宏定义。

#define的作用就是为字符串起一个名字。例如,如下代码:

#define YES 1#define PI 3.1415926

通常来说,宏名称通常会使用所有字母大写的形式。通过宏定义可以使用更有意义的名字代替原有字符串,例如上面使用PI代替了3.1415926。

关于宏定义,有如下注意点。

  1. 宏定义并不是C语句,因此不要在宏名称与字符串之间使用=进行赋值,而且宏定义也无须使用分号结束。
  2. 宏定义并不是变量,它甚至不是常量,因此不要尝试对宏名称进行赋值。
  3. 编译器对宏定义处理就是进行“查找、替换”——将所有出现宏名称的地方替换成该宏对应的字符串,因此,程序员需要自己保证宏定义是正确的。

执行宏定义之后,该宏的作用域为从定义该宏开始,直到源代码结束,如果希望提前结束宏定义,则可以使用如下语句:

#undef 宏名称

带参数的宏定义

除了定义简单的宏之外,还可以在宏定义中使用参数,这就是带参数的宏定义,带参数的宏定义可以定义更灵活的宏。

定义参数宏的语法格式如下:

#define 宏名称(参数列表) 字符串

使用带参数的宏来计算圆面积:

#import <Foundation/Foundation.h>#define PI 3.1415926#define AREA(r) PI * r * rint main(int argc, char * argv[]){    @autoreleasepool{        NSLog(@"请输入圆的半径:");        double radius;        scanf("%lg", &radius);        //使用宏执行计算        NSLog(@"圆面积:%g", AREA(radius));    }}

条件编译

通常来说,源代码中所有的代码行都会参与编译,最后生成执行性代码。但在有些时候,程序需要根据特点环境进行选择性编译——例如对于特定的设备,只编译设备相关的代码,这就可借助条件编译来完成。

C语言支持两组条件编译指令。
1. #ifdef#ifndef#else#endif
2. #if#elif#else#endif

使用#ifdef、#ifndef、#else、#endif执行条件编译。

#ifdef#ifndef#else#endif主要以宏定义为判断条件来对编译进行流程控制。

源程序文件:IfDefTest.m

#import <Foundation/Foundation.h>#define DEBUG //定义DEBUG宏int main(int argc, char * argv[]){    @autoreleasepool{        for(int i=0; i<10; i++){            #ifdef DEBUG                NSLog(@"调试输出:i的值为:%d", i);            #endif        }    }}

上述代码使用NSLog()函数进行调试时,调试语句放在#ifdef#endif之间控制,这表明只有在定义了DEBUG宏的前提下,才会编译这条调试语句。这样在开发阶段只要保持DEBUG宏,编译器就会编译这条NSLog( )语句,运行时将会产生输出;当需要发布该应用是,只要将DEBUG宏定义删除,编译器将不会编译这条NSLog( )语句,运行时就不会产生输出。
除此之外,甚至可以在源代码中完全不定义DEBUG宏,而是在编译时定义DEBUG宏,使用clang命令编译源程序时可使用-D选项定义宏。例如,使用如下命令编译程序即可指定DEBUG宏。

clang -fobjc-arc -framework Foundation -D DEBUG IfDefTest.m

由此可以总结出一条开发技巧:

将调试语句放在#ifdef#endif之间,以宏定义为判断条件加以控制,当发布应用时只要删除该宏定义就可使得调试语句不再被编译运行,使得开发人员不在需要逐行的删除这些调试语句。

使用#if、#elif、#else、#endif执行条件编译。

#if#elif#else#endif提供了更通用的条件编译,它可以对指定的表达式进行判断,据表达式的值决定是否要编译指定的语句。

#import <Foundation/Foundation.h>#define AGE 25int main(int argc, char * argv[]){    @autoreleasepool{        #if AGE > 60            NSLog(@"老年人");        #elif AGE > 40            NSLog(@"中年人");        #elif AGE > 40            NSLog(@"青年人");        #else            NSLog(@"少年人");        #endif    }}

条件编译指令的存在价值

可能有初学者会问一个问题:使用简单的if、else if之类的分支语句完全可以处理这些流程控制,为什么还要使用条件编译指令?

需要记住条件编译的处理时机——它是在编译器执行编译之前进行的预处理。这意味着:如果使用条件编译命令,那么不符合条件的程序语句根本不会被编译,也不会生成可执行指令;如果使用普通的条件分支语句,所有的代码都编译成可执行代码,而程序运行时需要根据条件决定执行哪些语句——这意味着使用普通条件分支语句生成的文件更大,而且执行的效率更低。

简单来说,能用条件编译处理的流程控制(如果分支条件只有数值、常量、宏变量),应该尽量使用条件编译进行处理。


#include与#import

C语言提供了#include来导入其他源程序,而Objective-C则提供了#import来导入其他源程序,而且#import更加好用。

#include指令的作用非常简单,它就是将指定的源代码插入到当前源代码的指点位置,从而有可能导致重复导入同一源代码。
Objective-C提供的#import更加智能:大部分时候,#import的功能和#include功能相似,但#import可以帮助程序员判断,避免重复导入的问题。因此,在Objective-C程序中通常推荐使用#import来导入源代码。

在大部分情况下,导入用户自定义的源程序都是使用双引号来包含源文件的,用于告诉预处理程序到一个或多个路径下(通常首先搜索当前文件所在路径,也可通过Xcode的项目设置来设置预处理程序的搜索路径)搜索指定的源文件。例如,如下语句:

#import "Person.h"

如果将要包含的源程序放在<>之间,则用于告诉系统只在特定的“系统”头文件路径中搜索被导入的文件,而不是在当前路径搜索。如下语句。

#import <Foundation/Foundation.h>

简而言之,如果导入用户自定义的源文件,那么在#import后使用双引号来包含源文件的文件名;如果要导入系统的源文件,则在#import后使用<>来包含源文件的文件名。

原创粉丝点击