Swiftにおけるインポートとリンクの復習

先日行われた iOSDC 2019 に参加してきました。

岸川さんによる「Swiftにおけるインポートとリンクの仕組みを探る」を聞いてきました。 ふわっとしか理解できていなかった部分だったので発表内容を復習しようと思い、改めて資料の内容を書き出し、 Xcodeで触ったりしながら自分なりに整理してみました。完全に理解したというには到底及ばないですが、以前に比べるとだいぶ理解が深まりました。分かりやすく発表をしてくださって大感謝です。

色々触りながら学びがあればどんどん加筆していこうと思っています。

誤っている箇所があれば指摘していただけると嬉しいです。

ライブラリについて

形式

  • FrameworkとLibraryの2種類がある。
  • Frameworkはバンドルを持ち、Libraryは持たない。
  • それぞれ、書かれた言語、実行環境のアーキテクチャ、プラットフォームにも依存する。
  • エラーになった場合に組み合わせが多いので解決が難しいが、1つずつ潰していけば大丈夫。

Framework

  • 拡張子が .framework のもの。
  • バンドルを持つ。
  • ダイナミック、スタティックがあるがアイコン同じなので見た目でわからない。
    • 新規作成した時のデフォルトはダイナミック。
    • BuildSettings の Mach-O を static にするとスタティックになる。
  • また、それぞれModuleを含む含まないの違いがある。

Library

  • バンドル、Moduleは持たない。
ダイナミック
  • 拡張子が .dylib のもの。
スタティック
  • 拡張子が .a のもの。

バンドル

  • 一定の規則に従ったディレクトリ構造のこと。
  • 例えば、Appバンドル、Projectバンドル、Settingsバンドル、Frameworkバンドルといったものがある。
  • FrameworkはFrameworkのディレクトリ構造に従ったもの。
  • バンドルはコード以外のものを含めることができる。

Module

  • Moduleディレクトリ配下のもの。
  • PublicなAPIの宣言がされている。
  • Swift でライブラリをインポートするためにはModuleが必要。
  • ObjC/CのLibraryをインポートするためには、ヘッダファイルを Module MapでModuleに変換する。
  • SwiftのLibraryではコンパイラがModuleを自動生成する。
  • ModuleはFrameworkバンドル外に置くこともでき、その場合には SWIFT_INCLUDE_PATHS にModule Mapがあるディレクトリの親ディレクトリを指定する。
    • Swift製の場合はわざわざ出す必要はないけどObjC製とかそもそもModule Mapがないときに有効。
  • Module Stability によってどのバージョンでコンパイルされたModuleもimportできるようになった。

Module MapとBridging Headerの違い

  • Module MapはBridging Headerの上位互換。
  • Bridging Headerはglobal importになってしまうので依存関係が分かりづらくなる。
  • Bridging Headerはアプリターゲットでしか使えない(Framework開発では使用できない)。
  • Module Map も自分で書くことができる。

インポート

  • 外部ライブラリが公開しているシンボル(クラス・関数など)を自分のコードベースで参照できるようにするための構文、言語機能。
  • 各ファイルをコンパイルする時点で解決される。
  • FrameworkやLibraryをインポートするということは、その中にあるモジュールを参照できるようにしているとういうこと。

リンク

  • ソースコードコンパイルして生成されたオブジェクトファイル、および外部ライブラリまとめて1つの実行形式ファイル、あるいはライブラリを生成すること。
  • 全てのソースコードコンパイルした後にリンカーが行う。
  • ダイナミック、スタティック全てのシンボルを解決する必要がある。
  • コンパイルして生成されたオブジェクトファイルのリンクは失敗しない。
  • ダイナミックリンクとスタティックリンクがある。

スタティックリンク

  • ビルド時にリンクが行われる。
  • ターゲットことに同じものが作られるのでシンボルが衝突するということが起こる。
    • これを防ぐのはなかなか難しい。

ダイナミックリンク

  • ビルド時には参照だけされて実行時にリンクされる。
  • 複数のターゲットから同じものが参照される。

リンクするための設定

Xcode で行う場合

  • Linked Frameworks and Libraries に追加する。
  • ただ、Xcode はデフォルトで Automatic Linking が有効になっているため、ここに追加しなくてもインポートするだけでリンク設定がされる。
  • Automatic Linking を無効にするためには OTHER_SWIFT_FLAGS に -Xfrontend -disable-autolink-framework -Xfrontend <FrameworkName> を指定する。

マニュアルでリンクするための設定

Frameworkの場合

  • FRAMEWORK_SEARCH_PATHS にFrameworkがある親ディレクトリのパスを指定する。
  • OTHER_LD_FLAGS に -l framework <FrameworkName> を指定する。
  • Dog.framework の場合には -l framework Dog

Libraryの場合

  • LIBRARY_SEARCH_PATHS にライブラリがある親ディレクトリのパスを指定する。
  • OTHER_LD_FLAGS に -l <LibraryName> を指定する。
  • libCat.a の場合には -lCat