Unityの端末ビルドでLINQとIComparerを組み合わせるとフリーズしてしまう問題
参考URL
業務でUnity開発をしていて、端末ビルドしたところ、iOSでもAndroidでも完全にアプリがフリーズしてしまう現象が発生していた。
XCodeやAndroidStudioで端末ログを見ても、フリーズに繋がるようなエラーは確認できなかった。
細かくログを仕込んでいつフリーズが発生するか確認した所、これがLINQのOrderByソートで起きていたことが分かった。

LINQのフリーズした箇所のコード
結論から言うとLINQのOrderByを使った匿名関数内で、IComparer継承クラスを使っていたのが問題だった。
C#としては正しい使い方のはずで、Editor上では問題が起きてなかったのに、何故か端末ではフリーズしていた。
業務上のコードをそのまま書くと守秘義務違反になるので、ぼかして概ね似たようなコードを書いていく。
例えば下のようなデータクラスとICompaerer継承クラスがあったとして、
using
System.Collections.Generic;
//インデックスと3つのステータスを持つシンプルなデータクラス
public
class
TestData
{
public
enum
DataStatus
{
Uninitialized,
Processing,
Completed
}
private
int
idx;
public
int
Idx
=>
idx;
private
DataStatus
status;
public
DataStatus
Status
=>
status;
public
TestData(int
idx,
DataStatus
status)
{
this.idx
=
idx;
this.status
=
status;
}
}
//2つのTestDataを比較し、StatusがProcessingの者は手前に、Uninitializedは真ん中に、Completedは後ろにまとまる様にする
//その上でそれぞれのStatusグループをIdx順に並べる
public
class
TestComparer
:
IComparer<TestData>
{
public
int
Compare(TestData
d1,
TestData
d2)
{
if(d1.Status
!=
d2.Status)
{
switch(d1.Status)
{
case
TestData.DataStatus.Processing:
return
-1;
case
TestData.DataStatus.Uninitialized:
return
d2.Status
==
TestData.DataStatus.Processing?
1
:
-1;
case
TestData.DataStatus.Completed:
return
1;
default:
return
0;
}
}
else
{
return
d1.Idx
>
d2.Idx
?
1
:
-1;
}
}
}
このTestDataに対し次のようなListを作り、ソートを実行したとする。
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class TestSort : MonoBehaviour
{
private void Start()
{
List<TestData> dataList = new List<TestData>()
{
new TestData(0, TestData.DataStatus.Completed),
new TestData(1, TestData.DataStatus.Uninitialized),
new TestData(2, TestData.DataStatus.Completed),
new TestData(3, TestData.DataStatus.Uninitialized),
new TestData(4, TestData.DataStatus.Processing),
};
var sortedList = dataList.OrderBy(x => x, new TestComparer()).ToList();
}
}
このOrdeByのコードでフリーズが起きていた。Editorでは正常に動くのに、何故かiOSとAndroidではフリーズする。
var sortedList = dataList.OrderBy(x => x, new TestComparer()).ToList();
匿名関数内でnewをしたのが不味かったのかな?と思い分離してみたが、それでも結果は変わらなかった。
var comparer = new TestComparer();
var sortedList = dataList.OrderBy(x => x, comparer).ToList();

フリーズの解決
色々調べて、Unityが端末ビルドする際に使っていてる
IL2CPP
が、
LINQと相性悪いらしいという記事を見つけた。
しかしLINQ自体はOrderByも含めこれまでも散々使ってきていて、フリーズは起きていなかった。
今回初めてやったことと言えば、LINQとIComparerを組み合わせたことだったので、とりあえず半信半疑でLINQをListの標準Sortに置き換えてみた。
//var sortedList = dataList.OrderBy(x => x, new TestComparer()).ToList();
var
sortedList
=
new
List<TestData>(dataList);
sortedList.Sort(new
TestComparer());
するとiOSでもAndroidでもフリーズが解消されたのを確認できた。今でもなぞ。
0
0