【问题标题】:StackOverflowException in nested loop in Unity c#Unity c#中嵌套循环中的StackOverflowException
【发布时间】:2020-04-30 06:04:37
【问题描述】:

在 Unity c# 中,我从 json 获取数据,并使用嵌套循环(如下代码)查找/比较游戏对象以在本地游戏对象中设置该数据。但是在达到一定数量的对象(> 275)后,我得到了 StackOverflowException 错误。基本上 80 次循环运行 10 次,运行 300 次,请参见下面的代码。(注意:此代码在 j

public void SetDataInObject()
    {
        if (j < ObjList.objects.Count)  //count is approx 300
        {
            for (int k = 0; k < allObjs.Length; k++)  // Length is 10
            {                
                stCalc = uiMana.floorStats[k].GetComponent<StatesCalc>();

                for (int m = 0; m < allObjs[k].floorObjs.Length; m++)  //Length is 80
                {
                    string serverObj = ObjList.objects[j].name;
                    string localObj = allObjs[k].floorObjs[m].gameObject.name;

                    if (localObj == serverObj)
                    {
                        ObjManager curObjManger = allObjs[k].floorObjs[m].GetComponent<ObjManager>();
                        //Logic to set data in local objects
                    }
                }                
            }
            j++;
            SetDataInObject();
        }
    }

【问题讨论】:

  • 如果递归方法最终有一个很深的调用堆栈,就会发生这种情况。您可能想尝试切换到迭代方法。
  • 另外j 似乎应该是一个局部变量。拥有一个基本上用作方法索引器的字段变量是一个坏主意。如果你需要控制它从哪里开始,那么你可以将它传递给函数。
  • 一种迭代方法是将第一行更改为 while(j &lt; ObjList.objects.Count) 并删除递归调用 SetDataInObject,但我看不出这有什么帮助 - 我看不到任何对象被创建将通过递归调用持续存在(但我整晚都没有睡,所以这当然是可能的!)
  • 同意 juharr .. 如果您可能更愿意将 j+1 作为参数传递给 SetDataInObject(int j)。或者没有for(var j = 0; j &lt; ObjList.objects.Count; j++) 的原因是什么?无论如何,我看不到任何可以更改该列表的行。还反复调用GetComponent 非常昂贵.. 如果可能的话,你应该事先缓存这些。

标签: c# unity3d stack-overflow nested-loops


【解决方案1】:

您的内部块运行大约 300 * 10 * 80 = 240000 次递归,多次存储一些值,并且还需要一些更长的解析才能了解那里做了什么。

实际上,我看不到您可以在方法中更改 ObjList.objects 的任何地方.. 所以反对简单地使用没有任何递归和“魔术”索引字段的闭环:

public void SetDataInObjects()
{
    for (var j = 0; j < ObjList.objects.Count; j++)
    {
        // This actually stays the same for all inner iterations until j is changed
        // so keep it around as long as possible to save resources
        var serverObj = ObjList.objects[j].name;

        for (int k = 0; k < allObjs.Length; k++)  // Length is 10
        {                
            stCalc = uiMana.floorStats[k].GetComponent<StatesCalc>();
            // Also this stays the same until k is changed
            // so keep the reference around to save access calls
            var currentObj = allObjs[k];

            for (int m = 0; m < currentObj.floorObjs.Length; m++)  //Length is 80
            {
                var localObj = currentObj.floorObjs[m].gameObject.name;

                if (localObj == serverObj)
                {
                    ObjManager curObjManger = allObjs[k].floorObjs[m].GetComponent<ObjManager>();
                    //Logic to set data in local objects
                }
            }                
        }
    }
}

您存储的变量是简单的类型,例如 intstringComponent 引用 .. GC 不应该关心太复杂的事情。

实际上,我认为完全不使用 jm 的索引会更好地解决这个问题

public void SetDataInObjects()
{
    foreach(var serverObj in ObjList.objects)
    {
        var serverName = serverObj.name;

        for (int k = 0; k < allObjs.Length; k++)  // Length is 10
        {                
            stCalc = uiMana.floorStats[k].GetComponent<StatesCalc>();
            // Also this stays the same until k is changed
            // so keep the reference around to save access calls
            var currentObj = allObjs[k];

            foreach (var floorObj in currentObj.floorObjs)  //Length is 80
            {
                if (localObjName.Equals(serverName))
                {
                    var curObjManger = floorObj.GetComponent<ObjManager>();
                    //Logic to set data in local objects
                }
            }                
        }
    }
}

这显然仍将运行内部块 300 * 10 * 80 = 240000 次,但使用在每次相应迭代后清除的局部变量,因此它们不会增加分配内存。

请注意,仍然重复调用GetComponent 非常昂贵,如果可能的话,您应该事先存储引用。

例如通过将floorStats 的类型直接设为StatesCalc[] 并将floorObjs 的类型设为ObjectManager[],假设您通过Inspector 引用它们,它将自动存储相应的类型引用,因此运行时不需要GetComponent

【讨论】:

  • 根据你们进行了优化,工作起来就像一个魅力,但我注意到的另一件事是,令人惊讶的是,这个问题只发生在 PC 中,而不是在移动设备中。移动设备较弱,这不是很奇怪吗?
猜你喜欢
  • 2013-05-30
  • 2021-03-20
  • 1970-01-01
  • 2017-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多