#ifndef、#define、#endif
前面我提到过,凡是前面带有#符号的,都是编译预处理指令,也就是在编译阶段就执行的语句。
#ifndef是“if not define”的缩写,是编译预处理三种功能(宏定义、包含、条件编译)中的一种——条件编译。
使用#ifndef可以避免如下错误:如果在h文件中定义了全局变量,一个C文件包含同一个h文件多次,如果不加#ifndef条件编译语句,就会出现变量重复定义的错误;如果加了#ifndef,则可以避免该错误。
#ifndef xxx
#define xxx
程序段1blabla
#endif
程序段2blabla
#ifndef的最主要目的是防止头文件的重复包含和编译。
#define就是用来进行宏定义。
#ifndef要和#endif配合使用,不然就报错。
#include、#import(#import<>、#import"")、@class
#include与#import
相同点:
1,都可以用在OC程序中起到导入文件的作用;
2,都有<>和""两种用法。
不同点:
1,#include是C语言的,当多个文件中包含同一个文件时,需要使用条件编译语句控制重复包含问题,否则就很容易出现递归包含;
2,#import是OC中对#include的改进版本,#import不需要写条件编译语句就可以确保引用的文件只会被包含一次,不会陷入递归包含的问题。
#import<>与#import""
<>是指从系统库中引用头文件,也就是从系统库目录(System Header Search Paths
)下查找,如果找不到,则结束查找。
“”是先从用户目录(User Header Search Paths)下查找文件,如果找不到,则继续在系统库目录(System Header Search Paths
)下查找文件。
一般而言,<>引入的是系统库的文件,""引入的是本地工程的文件。
#import与@class
#import会包含这个类的所有信息,包含各种变量和方法;而@class则会告诉编译器,其后面的名称是一个类的名称,现在无需知道该类的定义,后面会告诉使用者的。
在类的声明文件(.h文件)中,一般只需要知道被引用的类的名称就可以了,不需要知道其具体实现,所以在.h文件中一般使用@class来声明这个名称是类的名称;而在类的实现文件里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个所引用类的头文件。
如果两个类之间有循环依赖关系,即:A-->B,B-->A这样的相互依赖关系,如果在二者的.h文件中都使用#import来包含,就会出现编译错误,如下面代码所示:
//NewsViewController.h
#import <UIKit/UIKit.h>
#import "VedioViewController.h"
@interface NewsViewController : UIViewController
@property (nonatomic, strong)VedioViewController *vedioVC;
@end
//VedioViewController.h
#import <UIKit/UIKit.h>
#import "NewsViewController.h"
@interface VedioViewController : UIViewController
@end
此时就会报错,如下图:
解决该问题的方案就是:在类的.h文件中使用@class来声明引用类,然后在.m文件中再使用#import来导入引用类。
总结
之前我写过一篇本文主题的文章:Effective Objective-C 2.0——在类的头文件中尽量少引用其他头文件,该文章举的例子是错误的。下面我做一下阐述。
错误片段如下:
这里我说,在头文件中使用#import引入其他的类,很有可能会重复引入一些内容。可是通过前文我们可知,#import对比#include的一大优势就是不会重复引入相同的类。所以,不要在当前类的头文件中使用#import引入其他的类,因为如果引入类的头文件中也import了其他的杂七杂八的类,那么当前类就会引入许多根本用不到的类,这势必会增加编译时间。
所以,在头文件中是用#import导入引入类,会导致如下两个问题:
1,可能会引入许多根本用不到的内容,增加编译时间;
2,容易引起循环导入,进而导致编译错误。
因此,我们在类的头文件中少使用import引入其他的头文件,而是使用@class来声明一个类。
以上。