はじめに
iOSアプリ開発用パッケージマネージャーごとのStatic/Dynamicリンク設定とカスタマイズおよび各種調査方法について整理したのですが、スタティックとかダイナミックリンクってなんなのよ、ということもあるので、その前提知識としてiOSアプリ開発のための外部ライブラリのリンクの種類や配布/利用方法についても整理しておきます。
この記事では大きく分けて下記の2つのことを説明します
- 外部ライブラリのリンク種類
- スタティックリンク
- ダイナミックリンク
- 外部ライブラリの配布/利用方法
- Library
- Framework
- XCFramework
そしてこれは例えば、"スタティックリンクのFramework"や"ダイナミックリンクのFramwork"の組み合わせが存在するということです。
用語としてややこしいのは外部ライブラリの配布方法にLibraryとFrameworkがあることです。この記事では外部ライブラリと書いた場合、スタティック/ダイナミック関係なくLibrary/Frameworkの区別はしません。具体的には外部ライブラリ-> SVProgressHUD みたいなノリです。
私自身が勘違いしている可能性も高いし、もっといいまとめ方もあるかもしれないのでそれはそれでコメントか別にアウトプットしてもらえればありがたいです。
外部ライブラリリンクの種類
Appleの昔のドキュメントから
主にOSX用のドキュメントですが、スタティックリンクについて下記のように図示されています。
上記、重要なのはApplication codeの中にStatic librariesが含まれるということ。逆に下記で示されるダイナミックリンクではApplication codeにはDynamic library refrencesを含み、そのライブラリの実体はアプリとは別ファイルとして保持します。
上記Appleドキュメントを読むと、「ダイナミックリンクのライブラリは本体アプリサイズを抑える」 ことなどの解決策として存在するのがわかります。これは例えばUIがあるアプリはUIKit.frameworkとかをiOS側に持っておけば各アプリはそれを参照さえすればいいわけです。iOS 13からのSwiftUI.frameworkでもそうでしょう。iOS 13からiOS側でSwiftUIのフレームワークを持っているからiOS 13からSwiftUIを使える。
しかしiOSアプリとして我々がアプリ利用者に配布する際は外部ライブラリをダイナミックリンクとしても利用者がダウンロードする全体のアプリサイズは外部ライブラリを含んでいるので上記のドキュメントの 「ダイナミックリンクのライブラリは本体アプリサイズを抑える」 記述はしっくりくる理由にはなっていないのがわかります。
macOSでAtom.appを覗いてみる
ダイナミックリンクのライブラリについて想像しやすくするため、macOSのAtom.appの中を見てみます。
libffmpet.dylibやlibnode.dylibなどが同梱されており、これがダイナミックリンクされている、ということでしょう。iOSアプリに限らず、ダイナミックリンクのライブラリを同梱するのであればAtom.appのサイズは大きいのでこれ良い具体例ではないかもしれませんがニュアンスわかるでしょうか。これらのダイナミックリンクライブラリがmacOSにあってそこを参照するならたしかにアプリサイズはその分だけ小さくて済むはずです。
2つのリンク手段
外部ライブラリリンクの種類としてダイナミックとスタティックあるので2つの概要を整理します
- ダイナミックリンクライブラリ
- 概要
- 必要なときにロードする
- アプリの起動時またはランタイムにロードすることができる
- iOSアプリだと起動時になるのは?
- Dynamicローダ
- 起動時にアプリが実際に使用する外部シンボルのみを解決
- アプリの起動時またはランタイムにロードすることができる
- 別名
- Dynamic Shared Libraries
- shared objects
- Dynamically Lindked Libraries
- 必要なときにロードする
- メリット
- アプリのファイルサイズとメモリフットプリントの削減
- 動作
- Heap領域にDynamic Library Refrencesを持つ
- Dynamic Libraryのファイル自体は別途保持
- Heap領域にDynamic Library Refrencesを持つ
- 概要
- スタティックリンクライブラリ
- 概要
- スタティックリンカーを使ってリンクするとライブラリが実行ファイルにコピーされる
- スタティックリンカー
- オブジェクトコードを実行時に全体をメモリにロードする
- オブジェクトコードとは
- コンパイル済みのソースコードとライブラリコード
- オブジェクトコードとは
- オブジェクトコードを実行時に全体をメモリにロードする
- 別名
- Static archive Libraries
- Statick Lindked Shared Libraries
- 動作
- アプリ起動時にアプリ自体のコードとリンクされているStaticライブラリのコードを含めてアドレス空間に読み込まれる
- ヒープ領域でアプリ自体と一つのファイルになっている(.appに組み込まれている)
- アプリ起動時にアプリ自体のコードとリンクされているStaticライブラリのコードを含めてアドレス空間に読み込まれる
- 概要
混乱しそうなのはスタティックリンクは起動時に時間かかりそうだけど、ビルドの後半でスタティックリンカーによって、アプリと一体になっているからアプリ起動時間にダイナミックリンクに比べたら遅くなる要因は少ない
ライブラリ配布/利用手段
- Library
- 外部ライブラリリンクの種類
- スタティックリンク
- ダイナミックリンク
- 実体
- ビルド済みのソースコード
- 外部ライブラリリンクの種類
- Framework
- 外部ライブラリリンクの種類
- スタティックリンク
- ダイナミックリンク
- 実体
- ビルド済みのソースコード、画像リソース、ローカライズされた文字列、ヘッダー、リファレンス
- 例
- Foundation.framework
- UIKit.framework
- SwiftUI.framework
- 外部ライブラリリンクの種類
- XCFramework
- 実体
- 複数CPUアーキテクチャ別のFrameworkを1つにまとめたもの
- 実体
経緯
だいたいの経緯がわかってると、なぜこんな種類があるのかなんとなくわかるはずですので書いてみます。
- 2014
-
Xcode 6でiOS 8からダイナミックリンクのFrameworkをサポートした
- おそらく
- ダイナミックリンクのLibraryも同時にサポートされた?
- もともとはセキュリティ上の懸念があってできなかった?
- iOS 8からAppのExtensionが導入されたため懸念を払拭してやらざるを得なくなった?
- Embedded Frameworkもだいたいこのあたり
- おそらく
-
Xcode 6でiOS 8からダイナミックリンクのFrameworkをサポートした
- 2015
- 2019
- Xcode 11(WWDC19)あたりでXCFrameworkが登場
- Xcode 11からSwift Package ManagerがXcodeのGUIから設定できるようになった
- 2020
- Swift Package ManagerでXCFramework形式の配布ができるようになった
- 2021
- Carthage v0.37.0
-
--use-xcframeworks
を使ってXCFramework化
-
- CocoaPods v1.9.0
- XCFramework対応
- Carthage v0.37.0
おわりに
冒頭で組み合わせとしてスタティックリンクされたFrameworkがある、と書いてはいますが、これ昔のAppleのFrameworkに関するドキュメントを見るとFrameworkの説明としてダイナミックリンクの説明とそのメリットについて述べています。つまり、スタティックリンクなFrameworkは昔は存在しなかったのかなと思います。なぜならFrameworkのLibraryに比べたメリットはリソースを持てることだからですね。