Xcode6でヘッダーのモジュール化のすゝめ

  • 41
    Like
  • 0
    Comment
More than 1 year has passed since last update.

ヘッダーをモジュール化することによるメリット


  • モジュール化したヘッダーファイルはコンパイル(バイナリー化)されるので、コンパイル時間が短縮される。

ヘッダーのincludeの場合、コンパイラーはヘッダーがincludeされるたびに前処理とテキスト解析を行う。
従ってheaderファイルがM個あり、それぞれN個のファイルにincludeされていた場合、
上記のinclude処理はMxN回行われる事となる。

しかしモジュールのimport処理の場合は、pchファイルと同じようにヘッダーファイルを1回コンパイルしてバイナリー化するため、処理はヘッダーのコンパイル処理とモジュールの組み込み処理に分解することが出来、処理数はM+Nへと減らすことが出来る。


  • モジュール化されたヘッダーがincludeされた場合、clangは自動的にモジュールのimportを行ってくれる。

module.modulemapファイルでヘッダーをモジュール化するだけで、
自動的にモジュールとして読み込まれる。

暗黙的submodule宣言を使えば、ディレクトリ配下のヘッダーファイルをそのままファイル名を用いてモジュール化することが出来る。


  • ヘッダーを一つのファイルにまとめることが出来る。

module.modulemapファイルにはコメントも記述することが出来る。


  • マクロのincludeによる他ソースファイルへの破壊を防ぐことが出来る。

module間同士は互いに独立し、影響を及ぼさない。


  • 現在Appleから用意されているフレームワークはすでにモジュール化されているため、裏では自動的にモジュールとしてインポートされている。


  • モジュールをインポートすれば、フレームワークのリンクを追加する必要はなくライブラリー化して配布する際に、相手にフレームワークの追加をお願いする必要がなくなる。

AVFoundation.frameworkを使いたい場合は、
@import AVFoundation;
とモジュールのimportを行えば、AVFoundation.frameworkのリンクを追加する必要がなくなる。


ヘッダーのモジュール化方法


暗黙的submodule宣言で一括してヘッダーファイルをそのままモジュール化する方法

1. ソースディレクトリ配下にmodule.modulemapファイルを作成する。
finder modulemapファイル.png

modulemapファイルはheaderファイルと同様に、ターゲットに追加しないことに注意。

2. 暗黙的submodule宣言でディレクトリ配下のヘッダーファイル全てモジュール化する。

xcode modulemapファイル.png


module ModTest2 {
    umbrella ""
    explicit module * {
        export *
    }
}

ModTest2ディレクトリ配下のヘッダーファイルを全てモジュール化する。


ヘッダーをモジュール化した場合の注意1


  • モジュール化されたヘッダーはinclude/importした場合、Xcode(clang)は自動的にモジュールのインポートに差し替える。

  • モジュール間同士は独立している。

上記の理由によってヘッダーのincludeの場合は以下は問題なく動作するが、
a.hをモジュール化するとコンパイルエラーとなる。

a.h
NSString * const STR = @"This is a pen!"
main.m
#import <Foundation/Foundation.h>
#import "a.h"

int main (void) {

    NSLog(@"%@", STR);

    return 0;
}

以下のようにa.hをモジュール化する。

module.modulemap
module A {
    header "a.h"
}

上記でコンパイルを行うとa.hがコインパイル出来ずにエラーとなる。
(NSStringが解釈できない)

スクリーンショット 2015-02-28 21.33.32.png

上記はmain.mでヘッダーのinclude/importとは違って、
モジュールのインポートは互いに影響を与えないため、a.hをモジュール化すると、

ゆえに解決策として、a.hファイル内でFoundationモジュールをインポートする必要がある。

まとめ

上記のようにヘッダーのincludeとモジュールのimportは動作が異なるため、一部だけをモジュール化するというのは好ましくないと思われる。
しかし暗黙的サブモジュール宣言を使用すれば、ソースファイルが格納されているディレクトリ配下に、modulemapファイルを定義して配置するだけでヘッダーを全てモジュール化出来る。

それ以外に、SDKヘッダーで定義されているヘッダーをそれぞれモジュール化することも出来る。

またモジュールはclangの機能であるが、明示的にモジュールをインポートする必要が無いため、その他のコンパイラで使用される可能性があるコードは引き続きヘッダーのinclude/importを宣言するだけで構わない。またコンパイルする際にmodule.modulemapファイルをclangに渡す必要もない。

そして何より、すでにAppleが用意しているフレームワークは全てモジュール化されている。



モジュールマップ言語については、以下を参照してください。

clang modulesについて
http://qiita.com/ysn551/items/6f783af2b65c2c4d4631

公式リファレンス
http://clang.llvm.org/docs/Modules.html