pch让编译更快
在日常的开发中,有很多地方会用到Foundation和UIKit,使用之前需要先将头文件#import进来。与C语言中的#include相似,import会把头文件里的代码copy过来,只不过#import可以避免重复引用。比如,#import "Test.h"
等同于:
#ifndef Test_h
#define Test_h
#include "Test.h"
#endif
如果你的应用依赖于UIKit,那么每次编译应用都需要去编译UIKit,这会增加build的时间。<b>节约生命,从减少build时间开始。</b>
预编译头文件,顾名思义,是将头文件事先编译成一种二进制的中间格式。在整个编译过程中,只编译一次,并且会有缓存,如预编译头所涉及的部分不发生改变的话,在随后的编译过程中此部分不会重新进行编译,从而大大提高编译速度。
在iOS开发中,我们可以在xxx.pch中定义需要预编译的头文件,在Xcode 6 之前,自动生成的pch如下:
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#endif
pch的副作用
加入pch中的头文件,在项目其他地方要使用时,就不需要再import了。这可以算是一种便利,但它也是副作用的源头。切记:<b>pch的目的是提高编译速度,而不是让你少打几行import代码。</b>
- 不利于代码移植
如果同一份代码需要在多个项目中使用,过度使用pch会带来麻烦。假设项目A的pch中加了这样一行代码:#import <Security/Security.h>
,而你的某个组件使用了Security,将组件代码copy到项目B去可能编译不了,因为项目B的pch并没有添加Security.h - 隐式依赖关系
本来我们可以通过扫描xxx.h和xxx.m文件,找到xxx所依赖的模块。但是滥用pch会让这些依赖关系被掩盖,因为你的文件可能隐式地依赖了pch中定义的某些头文件。我在另外一篇文章架构设计中的循环引用中提到文件之间不应该循环引用,否则系统将越来越复杂和不可维护。
有节制地使用pch
在Xcode 6之后,新建项目已经不会自动生成pch文件了。
个人认为原因有两个:
- 因为它的副作用
- Modules的引入可以更好地替代预编译头
iOS 7之后,系统的Module都可以被"semantic import"。使用起来很简单,把原来的#import换成@import即可。比如:#import <Foundation/Foundation.h>
换成@import Foundation;
即可。
编译器遇到@import
时,会将预编译好的framework载入,同时也不需要到project settings里添加framework,系统会帮你做这些事情。<b>所有重复性的劳动都应该被自动化。</b>这些Module只会编译一次,因此已经可以不用pch了。
不过,pch也并没有完全退出历史舞台。有一些场景还是会使用到,比如你的每个文件都需要用到你定义的一些方法。加入pch的文件应该满足:
- 几乎所有文件都会用到。
- 不应该经常改动。宏定义,常量定义等不应该放在pch里,pch应该只有
#import
或者#include
。
有节制地用,宁可多敲几行代码,不要偷懒把很多东西都放pch里。