Modules and Precompiled HeadersDec 7, 2014 · 4 minute read
Apple announced Modules in WWDC 2013 so this post may be a little over due. What prompted me was noticing that Xcode no longer adds a Prefix.pch file to new projects by default which has a lot to do with modules.
#Import versus #Include
To understand why modules are useful it is worth first recapping the traditional way of working with frameworks and libraries. As I am sure everybody reading this knows it involves including (via a #include) the framework or library header in your source file and then at build time linking with the library.
The #import directive used by Objective-C is almost identical to the classic #include mechanism it inherited from the C programming language. The only difference is that it makes sure the same file is never included more than once. This is a small convenience to avoid the common C technique of including a header guard for the preprocessor:
/* someframework.h */ #ifndef _SOMEFRAMEWORK #define _SOMEFRAMEWORK /* body of someframework.h */ #endif
Unfortunately Objective-C did nothing to address other problems with the #include preprocessor mechanism. Having the preprocessor parse each header file in each source file quickly becomes slow and inefficient as the project grows as many source files include the same header files. The simple mechanism of text inclusion also leads to fragility as names from different headers collide.
Precompiled headers are a partial solution to the problem of slow build times. They speed up the time it takes to compile a project when all or nearly all source files need to include some common headers. For example, an iOS project is likely to include
Here is a typical
Prefix.pch file from an old project that imports UIKit and Foundation headers as well as checking for at least iOS 5:
#import <Availability.h> #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif #ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #endif
The ability to have something globally included by placing it in the
pch file is a useful but easily abused facility. Maintaining and optimising the prefix header file also puts a lot of the work on the developer that the tools should be handling for us. For another view on the ways developers can abuse prefix headers see 4 Ways Precompiled Headers Cripple Your Code.
A module offers a better way to work with system frameworks and libraries by replacing the preprocessor text inclusion mechanism with what Clang refers to as a semantic import. To import a module you use the @import declaration instead of the #include or #import preprocessor directives (note the semicolon):
When the compiler sees a module import it loads a standalone precompiled version of the framework. It also automatically takes care of linking to the module meaning you no longer need to manually add the framework in Xcode. Since the module is compiled just once there is no longer any advantage to including the framework header in prefix.pch. As a result the Precompile Prefix Header build setting now defaults to NO in Xcode.
Opting into using modules is as easy as setting the Apple LLVM 6.0 - Language - Modules Xcode build settings:
Both the Enable Modules and Link Frameworks Automatically settings default to YES in new Xcode projects. In fact once modules are enabled any #import or #include directives are automatically converted to @import declarations. This means you can adopt modules without having to make source code changes. For example a header file that has old style preprocessor imports for UIKit and Core Data frameworks:
#import <UIKit/UIKit.h> #import <CoreData/CoreData.h>
With modules enabled this is automatically mapped to import declarations:
@import UIKit; @import CoreData;
As a result of this automated conversion and the new Xcode defaults you may even be using modules without realising it if you have recently created a new project. One limitation of modules is that they are not available for user frameworks but the Apple system frameworks have been available as modules since iOS 7 (and OS X 10.9).
Adding a Pre-Compiled Header to a Project
Now that modules are available there is no need to continue to list system frameworks in a precompiled prefix header. If you need to add a prefix header to an Xcode project you can still do that (at least at time of writing with Xcode 6.1) by manually modifying the Precompile Prefix Header flag to YES and specifying the path in Prefix Header:
Some links for further reading: