[关闭]
@qidiandasheng 2016-12-27T02:00:05.000000Z 字数 3887 阅读 4380

iOS里的导入头文件

iOS理论


#import 和 #class

#import 是 Objective-C 导入头文件的关键字,完整地包含某个文件的内容。

@class 仅仅是声明一个类名,并不会包含类的完整声明。

之前有个问题就是说当两个类文件有循环依赖关系 ( A 引用 B , B 引用 A ) 时,需要用 @class
其实是没必要的,因为#import只会导入一次,就算你多个文件#import了同一个类,也只会导入一次,不会重复导入。所以使用#import也是没问题的。

只是如果你在类中只需要某个类的声明,不需要里面的属性啊方法之类的,那可以使用@class,不需要导入整个类的内容,这样也能节省编译时间。

#import<> 和 #import""

介绍

我们在#import的时候是怎么知道你的工程中有没有对应的类文件的呢?其实是设置build setting里的(User) Header Search Path路径,然后编译器去对应路径下找头文件的。

Header Search Paths 顾名思义就是用来存放 Project 中头文件的搜索根源,没有被add到项目里的头文件,可以通过配置Header Search Paths 来引入头文件,这样的好处可以不让project 包含的文件太多,便于管理。

浅显一点的区别是,编码时候通过 #include 引入头文件的方式有两种 <> 和 ""。<> 是只从 Header Search Paths 中搜索, 而 "" 则能从 Header Search Paths 和 User Header Search Paths 中搜索。换言之 ,假如你把 路径加到 User Header Search Paths 中,那么 你用 #include 的方式去引入对应的头文件,就会报错。 如果加到 Header Search Paths, 就没有问题了。

具体一点的区别是,<> 是从系统目录空间 (对应 Header Search Paths)中搜索文件, "" 是从用户目录空间(对应 User Header Search Paths)中搜索文件。如果你把路径加到 User Header Search Paths 中,而 <> 无法从系统目录空间中找到新加的路径,从而报错。

所以用cocoapods安装pod的时候,pod的路径都会设置在Header Search Path而不是User Header Search Paths,这样就算用#import<>引用也不会报错。

问题

一般我们在Pod里导入framework的里的头文件的话,是这样使用的#import <frameName/name.h>#import "frameName/name.h",而直接#import "name.h"会提示找不到文件,是因为Header Search Path里你只告诉了编译器这个framework的路径而没告诉他framework下面文件的路径。
Header Search Path里的路径后面会有recursivenon-recursive选项,表示是否递归读取文件路径,所以当你把这个值设置为recursive的话,那么#import "name.h"就不会报错了。

所以如果我在主工程要#import "name.h"这样使用pod里某个framework的文件的话,我就会在主工程里如下图这样设置:

如果是在某个pod里的话需要设置那个pod对应的build setting,那样的话每次pod update都需要去手动修改,太麻烦了,可以在podfile里像下面这样写:

  1. post_install do |installer|
  2. installer.pods_project.targets.each do |target|
  3. target.build_configurations.each do |config|
  4. if config.name == 'Debug'
  5. config.build_settings['USER_HEADER_SEARCH_PATHS'] = '${PODS_ROOT}/**'
  6. end
  7. end
  8. end
  9. end

思考

那是不是把所有的头文件导入都写成#import "frameName/name.h"这样的呢?那么就不用去设置Header Search Path寻找的路径为recursive了。我们手动指定了路径,省去了编译器递归寻找头文件的时间。

答案是否,如果你只在Pod里使用的话,不管你使用的是源码还是Framework都是可以用#import "frameName/name.h"的。但是如果不使用Pod集成的话,直接把源码导进工程里,#import "frameName/name.h"就会报错,只能使用#import "name.h"

#import 和 @import

介绍

关于@import是iOS 7之后的新特性语法,这种方式叫Modules(模块导入) 或者 "semantic import(语义导入)" ,是一种更好的头部预处理的执行方式,这iOS 7之后你能通过@import语法来导入任何的framework,Modules是一种将所有可执行的framework打包在一起,貌似这种方式比起传统的#import更安全和更高效。

而且另外一个最大的改进就是使用@import之后,你不用在project settings那里添加framework,系统会自动帮你加载上了,方便了很多,也避免了很多不必要的错误,例如忘记了加入framework而出现的 "Linker Error"。

你可以通过输入@import出现的自动提示来看看可以导入的framework列表,比如我把AFNetworing打包成了framework,那么在使用@import的时候如下是这样的:


其中那个自动提示的文件其实就是跟framework同名的frameworkName.h文件里导入的文件,所以如果没有这个同名的frameworkName.h文件的话,@import就找不到这个framework了。

@import 和 Umbrella Header

假设我创建了一个叫testLibFramework

我们能看到Framework目录下有一个Modules文件夹,这个文件夹里有个module.modulemap文件,我们看到这里面有这样一句umbrella header "testLib.h",umbrella有保护伞、庇护的意思。
也就是说Headers中暴露的testLib.h文件被放在umbrella雨伞下保护起来了,所以我们需要将其他的所有需要暴露的.h文件放到testLib.h文件中保护起来,不然会出现警告。@import的时候也只能找到umbrella雨伞下保护起来的.h文件。

具体关于Umbrella Header的介绍可以看这篇文章:iOS - Umbrella Header在framework中的应用。这里回答一个文章中提到的问题:在Framework对应target的Build Settings中,Defines Module的作用?

这里用CocoaPods里的配置来回答,当我们使用use_frameworks!的时候会生成Framework的target,然后在这个target的Build Settings里我们能看到Defines Module为YES,Module Map File指定了一个.modulemap文件。

也就是说Defines Module为YES的时候Framework里面会生成一个Modules文件夹来存放那个.modulemap文件,而为NO就不会有Modules文件夹。

你可以手动把Defines Module的值修改一下然后build,看一下Products目录下生成的Framework里面有没有Modules文件夹和对应的.modulemap文件。

问题

那么是不是没有这个跟framework同名的.h文件的话是不是就没办法导入文件了呢?答案是否,你可要使用#import "framework/name.h"来导入文件。

@import vs #import - iOS 7里有提到#import会帮你自动映射成@import来导入这个framework,而且如果你像我上面提到的User Header Search Path设置为recursive的话,那么提示就会显示framework下的所有头文件,反之则不会有任何提示,但是可以手动输入。
当你#import导入类文件的时候其实它也是转换成@import的,所以如果你那个同名的frameworkName.h文件里没有你要导入的那个类的话也会出现warning,但是编译还是能通过的。

注意:如果你的framework里没有同名的.h文件的话,那么用#import ""就不会有warning,它默认就会去搜索framework下的所有文件。
比如AFNetworking.frameworks里没有AFNetworking.h#import "AFNetworking/UIButton+AFNetworking.h"导入能成功,而且不会有warning。
但是@import AFNetworking;就会直接报错:Module 'AFNetworking' not found。因为它是根据那个同名的AFNetworking.h文件来判断有没有Module的。

参考

@import vs #import

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注