iOSアプリ開発用パッケージマネージャーごとのStatic/Dynamicリンク設定とカスタマイズおよび各種調査方法

# はじめに

iOSアプリ開発のためのパッケージマネージャー別のリンク指定を整理しておきます。

勘違いしやすいポイントとしては、Carthageを使ってもデフォルトでdynamicリンクなのでstaticリンクするよりもアプリ起動は遅くなります。つまりCarthageを使って事前にビルドしておくことで都度のビルド時間を短縮しても、何もしないとアプリ起動が遅くなるわけです。その他、ちょっとややこしい話ですが、CocoaPodsでは`use_frameworks!`にするとdynamicリンクなフレームワークになりますが、これはstaticリンクにすることもできます。

この記事では、自分のアプリでどのように設定するかのプラクティスではなく、それぞれのパッケージマネージャーのデフォルトがどうなっていて、設定でそれを変える方法とビルドされたライブラリ/フレームワークが本当に意図通りになってるか確認する方法について書いておきます。

StaticやDynamicとはなにかということについては[iOSアプリ開発のための外部ライブラリのリンクの種類や配布/利用方法](https://qiita.com/yimajo/items/d4cc70dc63743d775c76)として別にまとめています。

私自身が勘違いしている可能性も高いし、もっといいまとめ方もあるかもしれないのでそれはそれでコメントか別にアウトプットしてもらえれば助かります。

# パッケージマネージャー別のリンク

## 概要

- Carthage
    - デフォルト動作
        - **dynamicリンク**
    - staticリンクにするには？
        - 公式の[ドキュメント](https://github.com/Carthage/Carthage/blob/master/Documentation/StaticFrameworks.md)を参考
    - 参考
        - https://github.com/Carthage/Carthage/blob/master/Documentation/StaticFrameworks.md
- CocoaPods
    - デフォルト動作
        - （おそらく）**staticリンク**
            - かつてSwiftがstaticリンクでのライブラリをサポートしていなかったため`use_frameworks!`ができた
    - 設定
        - `use_frameworks!`
            - Pods v1.5.0から
            - デフォルト動作
                - **dynamicリンク** のフレームワーク化
    - staticリンクにするには？
        - `use_frameworks! :linkage => :static`
            - Pods v1.9.0から
            - **ターゲットごとにstaticリンク** にできる
        - plugin
            - プラグインでさらに細かくPodごとにも指定できる
                - https://github.com/microsoft/cocoapods-pod-linkage
    - 参考
        - https://blog.cocoapods.org/CocoaPods-1.5.0/
        - https://blog.cocoapods.org/CocoaPods-1.9.0-beta/
- Swift Package Manager
    - デフォルト動作
        - **自動でstaticかdynamic** かが決まる
            - 現状、[基本staticになる](https://zenn.dev/yimajo/articles/551737e57b6b7d#%E8%9B%87%E8%B6%B3%3A-rxswift-dynamic%E3%81%AA%E3%81%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
        - ライブラリ作成者側がPackage.swiftでtype指定し **static/dynamicリンクの強制** はできる
    - 参考
        - [Kingfisher](https://github.com/onevcat/Kingfisher/commit/ec62354ca2fc8b82f245a86e8ee58e7e23e861c6#diff-f913940c58e8744a2af1c68b909bb6383e49007e6c5a12fb03104a9006ae677e)
        - [RxSwift-Dynamicなどについて](https://zenn.dev/yimajo/articles/551737e57b6b7d#%E8%9B%87%E8%B6%B3%3A-rxswift-dynamic%E3%81%AA%E3%81%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

## CocoaPodsでのstatic/dynamicリンクを混在させる

Pods v1.9.0からターゲットごとにフレームワークをstaticにできるようになりました。

以下は実例。

```rb
source 'https://cdn.cocoapods.org/'

target :DynamicTarget do
    use_frameworks! :linkage => :dynamic
    
    pod 'DynamicPod' # This will be linked dynamically
end

target :StaticTarget do
    use_frameworks! :linkage => :static

    pod 'StaticPod' # This will be linked statically
end
```

### 参考

https://medium.com/microsoft-mobile-engineering/mixing-static-and-dynamic-linking-in-cocoapods-83b6d4252c59

# ライブラリが指定のリンク形式かを調べる

- otoolでアプリのバイナリから調べる
    - otool
- ビルドされたライブラリから調べる
    - file

## otoolでビルドされたアプリのバイナリからDynamic Linkしているファイルを調べる

例

```bash
$ otool -L DerivedData/実行アプリバイナリ
```

## fileコマンドで確認する

例

```bash
$ file ./Carthage/Build/iOS/ReactiveCocoa.framework/ReactiveCocoa
```

- staticリンクになってれば
    - `current ar archive`
- dynamicリンク
    - `Mach-O dynamically linked shared library`


# アプリの起動時間を計測する

- DYLD_PRINT_STATISTICS
    - （Xcode 13からは使えなくなった）
- Instruments

## DYLD_PRINT_STATISTICSで起動時の時間表示

（DYLD_PRINT_STATISTICSはXcode 13から使えなくなりました）

ライブラリ/フレームワークによってはめちゃくちゃ遅いのがstaticリンクにすることでかなり起動速度が改善したりもしますが、どのDynamicリンクが遅いのがわかる方法もあります。

Xcodeのスキーマ設定のあたりから、Environment Variablesにて`DYLD_PRINT_STATISTICS`に1かYESを設定してビルド後にアプリ起動すると、Xcodeのコンソールにdynamicリンク時間を計測して下記のような表示がされます。

```
Total pre-main time: ＜ここがトータル＞ milliseconds (100.0%)
         dylib loading time: 188.92 milliseconds (38.6%)
        rebase/binding time: 141.32 milliseconds (28.9%)
            ObjC setup time:   9.88 milliseconds (2.0%)
           initializer time: 148.68 milliseconds (30.4%)
           slowest intializers :
             libSystem.B.dylib :   3.18 milliseconds (0.6%)
    libMainThreadChecker.dylib :  42.47 milliseconds (8.6%)
                HogeHogeSample :  69.27 milliseconds (14.1%)
                          ... :  23.87 milliseconds (4.8%)
...

```

例えば上記だとHogeHogeSampleというのがが遅いことがわかります。

## XcodeのInstrumentsを使う

- 注意点
  - 実機でないとWWDC19のApp Lifecycleは測定されません
  - dynamicリンクを調べようとしてもUnknownになって項目名が見えないプロジェクトもある


https://developer.apple.com/wwdc19/423

![スクリーンショット 2021-10-10 18.12.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/69450002-7dd7-9553-e053-e32f19e1fb4a.png)


### ついでにInstrumentsのみかた

![スクリーンショット 2022-04-11 13.08.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/e4b93d59-0d76-8242-99e0-502407388380.png)

![スクリーンショット 2022-04-11 13.08.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/39d13a26-c681-234b-ec87-673f4bb13b5d.png)

![スクリーンショット 2022-04-11 13.08.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/3948846d-5083-4854-82bb-e28132a46a01.png)

![スクリーンショット 2022-04-11 13.08.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/09cb756a-2805-7219-5d99-66ba3fe9a11a.png)

![スクリーンショット 2022-04-11 13.08.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/0144351a-11d7-db2f-0160-28f5c20f66dc.png)

![スクリーンショット 2022-04-11 13.08.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/629f3889-8224-105f-e3fc-131503763635.png)

![スクリーンショット 2022-04-11 13.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/49e5b6f1-4d52-178d-9cdd-da7cf5b8f069.png)