在2013年的苹果年度大会上,苹果在oc的性能改进上大大的变化之一就是加入了模块(Modules)。
-
文件编译问题的存在性----编译时间过长
在了解模块(Modules)之前,需要先了解一下oc的#import机制。通过使用#import,用来引用其他的头文件。熟悉C或者C++的人可能会知道,在C和C++里是没有#improt的,只有#include(虽然GCC现在为C和C++做了特殊处理,使#import可以被编译),用来包含头文件。#include做的事情其实就是简单的赋值粘贴,将目标.h文件中的内容一字不落地复制到当前文件中,并替换掉这句inclue;而#import实际上做的事情和#include是一样的,只不过Objective-C为了避免重复引用可能带来的编译错误(这种情况在引用关系复杂的时候很可能发生,比如B和C都引用了A,D又同时引用了B和C,这样A中定义的东西就在D中被定义了两次,造成重复)而加入了#import,从而保证每个头文件只会被引用一次,仔细研究一下,#import的实现是通过对#define一个标志进行判读,然后再引入#define这个标志,来避免重复引用的。
实质上,#import也是复制,粘贴。这样就带来了一个问题:当引用关系很复杂或一个头文件被非常多的文件引用时,编译器引用所占的代码量就会大幅上升(因为被引用的头文件在各个地方都被复制了一遍)。
在编写oc代码中,估计很多人已经写了一千遍或者更多的#import语句。
按照上面所说,这意味着,对于UIKit框架,通过计算所有行的全部UIKit中的头,就会发现他相当于冲过11000行代码,在一个标准的iOS 应用中,就会在大部分文件中导入UIKit,这意味着每一个文件最终编程11000行。这是不够理想的,更多的代码以为着更长的编译时间。 -
预编译头文件(Pre-compiled Headers)处理方式----不实用
理论上讲,解决这个问题可采取C语言的方式,引入编译头文件(Pre-compiled Headers,PCH),即把公用的头文件放入预编译头文件中进行编译。通过在编译的预处理阶段,预先计算和缓存需要的代码;然后在真正编译工程时将预先编译好的产物加入到所有待编译的源文件中,来加快编译速度。比如iOS开发中Supporting Files组内的.pch文件,就是一个预编译投文件。默认情况下,它引用了UIKit和Foundation两个头文件,这是在iOS开发中基本上每个实现文件都会用到的东西。如果开发的应用是使用iOS5之前的SDK,那么#warning将通知他们。UIKit和Foundation文件是stockPCH的一部分。因为在应用程序里的每一个文件将使用Foundation,并且大部分会使用UIKit。因此,这些都将被很好地添加进了预编译头文件,以便于在自己的应用程序中预先计算和缓存这些文件的编译文件。
但维护项目的预编译头文件是很棘手的。利用预编译头文件虽然可以加快编译时间,但是这样面临的问题是,在工程中随处可用本来不该访问的东西,而编译器也无法准确给出错误或者警告,无形中增加了出错的可能性。
-
利用模块(modules)来解决历史问题----事半功倍
模块(Modules),第一次在OC中公共露面实在2012LLVM开发者大会上。
模块 (Modules),封装框架比以往任何时候都更加清洁。不在需要预处理朱行地用文件的所有替换#import指令。相反,一个模块包含了一个框架到自包含的块中,就像预编译文件预编译的方式一样提升了编译速度。并且不需要再预编译头文件声明自己要用到哪些框架,使用Modules简单的获得了速度上的提升。模块还有其他方面的有点,在下列的操作代码的步骤过程中,都可以感受到模块的特性。
(1)在使用框架的文件中添加#import。
(2)用框架写代码。
(3)编译。
(4)查看链接错误。
(5)忘记链接的框架。
(6)添加忘记的框架到项目中。
(7)重新编译。
忘记链接的框架是编写程序代码中经常会犯的一个错误。利用模块就能解决这个问题。一个模块不仅告诉了编译器哪些文件组成了模块,而且还告诉编译器什么需要链接。这样就不用去手动链接框架了。虽然这是一个小事,但是能让开发变得更加简单,这就是一件好事。
4.开启使用模块
模块的使用相当简单。对于存在的工程,第一件事情是使这个功能生效。可以在项目的Build Setting 中通过搜索Modules找到这个选项,将Enable Modules选项设为Yes。
在默认情况下,模块功能所有的新工程都是开启的,但是应该在自己所有存在的工程中都开启这个功能。Link Frameworks Automatically选项,可以用来开启或者关闭自动框架链接的功能 。
一旦模块(Modules)功能开启,就可以在自己代码中使用它了。要这样做,对以前用到的语法要有一点小小的改动,那用@import代替#import:
@import UIKit;
@import MapKit;
@import iAd;
另外,只导入一个框架中自己需要的部分也是可以的。例如,只想导入UIView,就可以这样写;
@import UIKit.UIView;
使用模块功能就是这么简单。技术上,自己不需要把所有的#improt都换成@import,因为编译器会隐式的转换他们,但是还是建议尽可能的用新的语法来编写代码。
注意 Xcode 5.0的模块功能还不支持自己的或第三方的框架。目前Xcode 6 的测试版都出来了,在很多Xcode的新版本中,都支持模块功能。
要点######
(1)#include 和 #import,其根本就是简单的复制,粘贴。将目标.h文件中的内容一字不落的复制到当前的文件中,后者可以避免多次的重复引用。
(2)以预编译头文件的方式,虽然可缩短编译的时间,但其维护棘手,不利于广泛应用。
(3)模块功能,其应用不仅仅表现于编译的速度加快,同事在链接框架方面也非常好用。
(4)启动模块功能后,编译器会隐式地把所有的#import都转换成@import。