何個か修正を追加してUnityEditor自体やいくつかのアセットをバージョンアップしてiOSビルドしたところ途中でフリーズして動かなくなる事態に見舞われました。
問題箇所
適当なint
の値が与えられているFooEnum
というEnumの3桁目までの値の大小を比較してくれるComparer
をOrderBy
で使おうとしたらダメでした。
public void main() {
_map = new Dictionary<FooEnum, Bar>();
// _mapに値を代入
foreach(var tuple in
_map
.OrderBy(pair => pair.Key,
Comparer<FooEnum>.Create(
(left, right) =>
{
var lv = (int) left;
var rv = (int) right;
return rv % 1000 - lv % 1000;
})) )
// foreachで行う処理
}
修正版
別にIComparer
を継承したクラスを作ってやると解決しました。
public void main() {
_map = new Dictionary<FooEnum, Bar>();
// _mapに値を代入
var comparer = new FooEnumComparer();
var sorted = new SortedDictionary<FooEnum, Bar>(
_map
.ToDictionary(pair => pair.Key,
pair => pair.Value), comparer);
foreach(var tuple in sorted) // foreachで行う処理
}
public class FooEnumComparer : IComparer<FooEnum>
{
public int Compare(FooEnum left, FooEnum right)
{
var lv = (int) left;
var rv = (int) right;
return (rv % 1000 - lv % 1000) * 1000 + (rv - lv);
}
}
原因
LINQとか、Enumとかをil2cppと組み合わせると死ぬときもあるみたいな話だと思うのですが、具体的にこれがダメだからということはわからず……。
公式マニュアルの「クロージャと匿名メソッド」の項に、
C#内のメソッド参照は全て参照型であり、したがってヒープに割り当てられるということです。
https://docs.unity3d.com/ja/current/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html
匿名メソッドをクロージャに変換すると、クロージャを受領するメソッドへ渡すために必要とされるメモリ量が大幅に増加します。
とあります(一部省略)。
この辺……?
特定に至るまで
特にエラーメッセージをはいてくれることもなく、UnityEditor上では元気に動いているのでしばらく原因がわかりませんでした。
粒度細かくビルドしていくのが大事ですよね。アジャイルアジャイル。
実機ではフリーズしてしまうだけで情報が得られなかったので、UnityCloudBuildではなく手元でXcodeを使ってビルドして、そこに出てくるログを読んだり、一時停止をすることで各スレッドがどこまで進んでいるのかを見てみました。
すでにC++のコードに変換されているものを読まないといけないのでよくわからなかったのですが、止まっている部分を見ると、ComparisonComparer<T> : IComparer<T>
のような文字列が表示されていたので、自分の前回のビルドからの変更箇所と照らし合わせて特定しました。
結構時間かかったな……。