OC import探究

参考文章:iOS里的导入头文件

在项目二进制化的过程中,打包framework经常出现头文件报错,主要缘于项目组件化完成后,因为文件的移动导致头文件引用方式不对,特此总结下import原理

#import<> 和 #import””

#import<>从Build Setting–>Search Paths–>Header Search Paths路径去检索头文件(Always Search User Paths 已经被标记废弃)

#import" "从Build Setting–>Search Paths–>User Header Search Paths–>Header Search Paths路径去检索头文件

新建一个TestHeaderImport项目,Podfile:

1
2
3
4
5
platform :ios, '9.0'
target 'TestHeaderImport' do
pod 'Masonry'
end

更新过后

User Header Search Path为空,Header Search Path

1
2
3
$(inherited)
"${PODS_ROOT}/Headers/Public"
"${PODS_ROOT}/Headers/Public/Masonry"

从上图可以看到此时<>引用是会有智能提示的,但引号引用没有智能提示,主要是因为Search Path的原因,但使用引号引用也不会报错,因为在编译时引号引用除了去查User Header Search Paths还会去寻找Header Search Paths

如果此时把Header Search Path清空,那么<>引用也不会有智能提示

如果此时把User Header Search Path设置为上面配置,那么引号引用会智能提示和<>引用并不会

疑点:即使User Header Search PathHeader Search Path都删除的话,仍然可以编译通过

$(inherited)代表继承,此处加上这个配置代表继承上级配置,xcode继承链

Resolved <- Target <- xcconfig <- Project <- iOS Default

可以看到Target中如果配置了$(inherited)那么将从xcconfig、Project、iOS Default中同项配置继承过来

其中cocoapods会自动生成xcconfig文件

我们也可以自定义自己的xcconfig文件,创建好.xcconfig文件在下图选中即可

我们将cocoapods生成的xcconfigsearch_path删除

1
2
3
4
...
HEADER_SEARCH_PATHS = $(inherited)
...
OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Masonry"

发现仍然能编译通过,后来发现xcconfig中的OTHER_CFLAGS也配置了路径,将其删除后,发现此时编译报错了。

OTHER_CFLAGS

-isystem dir

Search dir for header files, after all directories specified by -I but before the standard system directories. Mark it as a system directory, so that it gets the same special treatment as is applied to the standard system directories. If dir begins with =, then the = will be replaced by the sysroot prefix; see –sysroot and -isysroot.

-isystem 修饰的路径,会打上系统路径的标,所以此处设置也会生效。

我们平时使用的时候,在依赖自己模块的头文件时,使用引号引用,在依赖其他模块的头文件时,使用<>引用

首先在依赖自己模块的头文件时,使用引号引用,因为尖括号引用会直接报错,原因是尖括号引用不会搜索当前文件所在的路径

其次在依赖其他模块的头文件时,使用<>引用

  • cocoapods会将依赖的库放进HeaderSearchPath
  • 在依赖二进制包时,单纯使用引号头文件会报错

因为在header search path中指定了头文件目录,单独引入头文件会找不到

上图中后面的non-recursive代表不递归查找指定路径,解决头文件报错可以使用以下几种方案:

  1. 可以将此项设置改为recursive,但是这样会导致:
    • cocoapods默认配置为non-recursive,会导致重复工作,所有此类依赖都需这样设置
    • 设为recursive会降低编译效率
    • 如果项目后期使用二进制依赖,会导致文件引用问题,除非自定义二进制打包脚本
  2. 使用路径/文件名的方式,其中使用引号和<>都可以,但推荐使用<>,因为可以提高编译效率,具体原因上文已经解释
    1
    2
    #import <UMMobClick/MobClick.h>
    #import "UMMobClick/MobClick.h"

在引入头文件时,发现下面写法也是可以的,原因参考非framework引用,类似原来是<A/A.h>,其中A.h 又被包了一层B,最终变为<A/B/A.h>

1
2
#import <UMengAnalytics-NO-IDFA/UMMobClick/MobClick.h>
#import "UMengAnalytics-NO-IDFA/UMMobClick/MobClick.h"

虽然上面引用不会报错,但不建议这样使用,因为当时在做项目二进制依赖时,这样使用会导致报错,因为后面一直未曾重现,留待再次遇见时探究原因。
还可以想到的一个原因便是,如上面引入的UMengAnalytics-NO-IDFA分为多个版本的二进制包,当在不同需求下依赖不同二进制是,代码里需要更改头文件引入

1
2
#import <UMengAnalytics-AA-IDFA/UMMobClick/MobClick.h>
#import <UMengAnalytics-XX-IDFA/UMMobClick/MobClick.h>


了解了import原理后,对目前打包二进制时报的头文件错误有一个清晰的认知,所以下一步要做的便是将库import方式做些更改,将依赖别的库的头文件引用方式改为<>引用,最终实现所有库的二进制化,大大提升开发编译效率

最后附上更改脚本:header_change