今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

iOS ビルド時間の最適化

背景#

現在の App プロジェクトは大きくありませんが、クリーンビルドには 200 秒以上かかるため、合理的ではないと感じました。そのため、問題を調査しました。

通常、コンパイル時間の最適化は次の 3 つの部分に分けられます。

  • Xcode のビルド設定の最適化
  • コードまたは関数のコンパイル時間の最適化
  • サードパーティライブラリのコンパイル時間の最適化

ここでは、上記の 3 つの部分に基づいて順番に調査します。

実装#

Xcode のビルド設定の最適化#

使用しているのは Xcode 13.4 で、インターネットで見つけた情報によると、Xcode の「New Build System」の設定や「Build Settings」での「Debug Information Format」の設定は必要ありません。デフォルトで適切な設定になっています。また、「Optimization Level」の設定は、コンパイル速度を向上させることができますが、デバッグには適していませんので、ここでは設定しません。したがって、この最適化には何も対処しません。

コードまたは関数のコンパイル時間の最適化#

この部分は主に Swift に対して行います。まず、コンパイルに時間がかかるメソッドを表示するために、次の設定を「Build Settings」の「Other Swift Flags」に追加します。これにより、コンパイル時間が 200ms を超える関数や、型チェックが 300ms を超える関数に対して警告が表示されます。ここでの 200ms は、プロジェクトの実際の状況に応じて設定できます。


-Xfrontend -warn-long-function-bodies=200
-Xfrontend -warn-long-expression-type-checking=200

ここで修正した内容は次のようなものです:

最適化前のコード例 1:


let count:Int = Int((self?.listParamItem.pageSize ?? 0) * ((self?.needRefreshPageNum ?? 0) - 1))
let endIndex = count + Int(self?.listParamItem.pageSize ?? 0) - 1
if (self?.dataList.count ?? 0) > endIndex {

上記のコードでは、オプショナルの値と??演算子を混在させて型変換を行い、それから比較を行っています。コードは正しいですが、コンパイルには本当に時間がかかります。これらのメソッドは 500ms を超えています。これらに対しては、次のコードに修正し、コンパイル時間を 100ms 以下に短縮しました。


if let self = self {
  let count: Int = self.listParamItem.pageSize * (self.needRefreshPageNum - 1)
  let endIndex: Int = count + self.listParamItem.pageSize - 1
  if self.dataList.count > endIndex {
}

最適化前のコード例 2:


let dic = [
    "aaa": xxx ?? yyy, 
    "bbb": ["ccc": "xxx", "eee": 5],
    "ddd": 5
]

上記のコードは問題がないように見えますが、コンパイル時間が 200ms を超えている可能性があります。これは型推論によるものです。次のコードに修正すると、コンパイル時間が 200ms を超えなくなります。


var dic1: [String: Any] = [:]
dic1["ccc"] = "xxx"
dic1["eee"] = 5

var dic2: [String: Any] = [:]
dic2["aaa"] = xxx
dic2["bbb"] = dic1
dic2["ddd"] = 5

最適化前のコード例 3:


if type == .aaa ||
type == .bbb ||
type == .ccc ||
type == .ddd ||
type == .eee ||
type == .xxx {
  doSomething()
} else {
    doAnotherThing()
}

上記のコードは見た目が良くないだけでなく、コンパイルにも時間がかかります。次のように修正します:


switch type {
case .aaa,
.bbb,
.ccc,
.ddd,
.eee,
.xxx: 
  doSomething()
default:
    doAnotherThing()
}

最適化前のコード例 4:


let fontAdd: CGFloat = 14.0

protocolBtn.snp.makeConstraints { make in
    make.left.equalTo(agreeLabel.snp.right).offset(1)
    make.centerY.equalTo(checkBtn.snp.centerY)
    make.width.equalTo(kTransitionW(150 + fontAdd * 10))
}

上記のコードでは、小数と整数の混合演算を行い、Swift にどの型が必要かを推論させています。次のように修正すると、コンパイルの警告が消えます:


let fontAdd: CGFloat = 14.0
let width: CGFloat = 150.0 + fontAdd * 10.0

protocolBtn.snp.makeConstraints { make in
    make.left.equalTo(agreeLabel.snp.right).offset(1.0)
    make.centerY.equalTo(checkBtn.snp.centerY)
    make.width.equalTo(kTransitionW(width))
}

最後に、特定のメソッドが非常に長くなり、コンパイル時間が超過する場合は、複数のサブメソッドに分割して解決します。

サードパーティライブラリのコンパイル時間の最適化#

プロジェクトでは CocoaPods を使用してサードパーティライブラリを統合していますので、クリーンビルド後には常にサードパーティライブラリが再コンパイルされます。Xcode でコンパイル時間を表示するために、ターミナルを開き、以下のコマンドを実行して Xcode を再起動します。


$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES

その後、クリーンビルドを行うと、次のように詳細なコンパイル時間が表示されます:

コンパイル時間

ここでの「ZLPhotoBrowser」は、Pod を使用してインストールされたサードパーティライブラリであり、このライブラリのソースファイルのコンパイルに 30 秒かかっています... このページでは、各ライブラリのコンパイル時間とプロジェクトのコンパイル時間を詳細に確認できます。コンパイル時間が長いサードパーティライブラリについては、「Carthage」方式でインポートすることをおすすめします。Carthage の使用方法を参考にしてください。「Carthage」は使用するサードパーティライブラリを「xcframework」としてダウンロードし、生成された「xcframework」をプロジェクトにインポートするため、クリーンビルド後に再コンパイルする必要はありません。これにより、コンパイル時間を節約できます。

結論#

コンパイル時間の最適化についてのまとめは次のとおりです:

  • Xcode のビルド設定の最適化 - 新しいバージョンの Xcode では設定の変更は必要ありません
  • コードまたは関数のコンパイル時間の最適化 - 型推論や複雑な計算の最適化、演算子の使用に注意する必要がありますが、すべてのコードのコンパイル時間が超過した場合に最適化する必要はありません。コンパイルの最適化とコードの簡潔さ、エレガントさ、Swift の特徴の間でバランスを取る必要があります。
  • サードパーティライブラリのコンパイル時間の最適化 - この最適化は推奨されます。変更しないサードパーティライブラリについては、「Carthage」方式でインポートすることで、再コンパイル時間を節約できます。

最適化を行う前に、まずプロジェクトのコンパイル時間がどこで遅くなっているかを分析し、サードパーティライブラリのコンパイル時間が長いのか、プロジェクトのソースファイルのコンパイル時間が長いのかを分析し、ステップ 2 またはステップ 3 に重点を置くかを決定してください。

参考#

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。