【Unity游戏开发】记录个奇奇怪怪的bug

今天在firebase后台发现一个数量蛮多的报错,大概长这样子:在这里插入图片描述
一个空引用的问题,经过分析定位,还算比较顺利的推测出了问题。我的消息系统里有一个消息在一个对象上只注册却遗漏了注销。 这样该对象销毁的时候,再次发这个消息,就会造成报错。但是有趣的点在于,我发送消息的代码长这样:在这里插入图片描述
其中IMessage是我消息类的接口,所有想要接受消息的类都需要继承这个接口在这里插入图片描述

很明显的问题是:我判空了呀!就算我之前消息遗漏了注销,但是理应我这里会把空的排除掉,就进不去红框里那个函数呀。于是我开始模拟报错情况,并且打上了断点。
在这里插入图片描述
好!进来了,我们看到消息对象中很明显前两个元素是null,接下来往下走,直接走到该元素:
在这里插入图片描述

进来了!而且断点明显显示IMsg是”null“ 但是”无视“了这个判定走到了下一步,这是咋回事啊?
第一反应,这个断点里标的null是带双引号的,会不是其实他是一个字符串"null"?不过那也太离谱,我这个对象很明显不是字符串。是一个继承了这个消息接口的类:
在这里插入图片描述
我们看到这个物体已经被销毁了,所以这里这样提示是很合理的,但是这个东西为什么不是纯粹的NUll?

于是我猜想:我的消息对象列表记录了这些Mono脚本的引用,但是Mono脚本所依附的Gameobject被销毁了,但是Mono脚本的引用还在,就不会成功==null判定,而是变成了带双引号的"null"。

然后开始证实我的猜想,写了如下测试代码:

 List<MergeItem> Tests = new List<MergeItem>();
    private void Update()
    {
    
    
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
    
    
            //第一步 随便新建十个物体 挂上Mono脚本 MergeItem
            for(int i = 0; i < 10; i++)
            {
    
    
                GameObject obj = new GameObject("TestMono"+i);
                Tests.Add(obj.AddComponent<MergeItem>());
            }
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
    
    
            //第二步  随机销毁一个幸运儿 这里删除下标为5的 gameobj 注意要销毁的是gameobject不是脚本
            DestroyImmediate(Tests[5].gameObject);
        }
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
    
    
            //第三步   依次打印出来看看输出啥
            for(int i = 0; i < Tests.Count; i++)
            {
    
    
                IMessage mergeItem = Tests[i];
                if(mergeItem!=null)
                    Debug.LogError(Tests[i] + "    ");
            }
        }
    }

然后再启动游戏,依次按下数字 1,2,3,结果如下:在这里插入图片描述
幸运儿TestMono5已经被销毁了,但是打印没打印出来!也就是说他在mergeItem!=null这个判定被拦下来了,也就是说这样Gameobect销毁,他上边的脚本也是会乖乖变成null的,我的猜想是错的!

那是为什么呢?我想,难道是因为我消息系统里获取的是接口而不是直接获取的类?
于是改改测试代码,如下:

 List<IMessage> Tests = new List<IMessage>();
    private void Update()
    {
    
    
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
    
    
            //第一步 随便新建十个物体 挂上Mono脚本 MergeItem
            for(int i = 0; i < 10; i++)
            {
    
    
                GameObject obj = new GameObject("TestMono"+i);
                Tests.Add(obj.AddComponent<MergeItem>());
            }
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
    
    
            //第二步  随机销毁一个幸运儿 这里删除下标为5的 gameobj 注意要销毁的是gameobject不是脚本
            DestroyImmediate((Tests[5] as MergeItem).gameObject);
        }
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
    
    
            //第三步   依次打印出来看看输出啥
            for(int i = 0; i < Tests.Count; i++)
            {
    
    
                IMessage mergeItem = Tests[i];
                if(mergeItem!=null)
                    Debug.LogError(Tests[i] + "    ");
            }
        }
    }

改成了在列表里不存类,和项目里一样存接口,实验结果:
在这里插入图片描述
这回不一样了,打印了十条结果,在第五条打印出了”null“,也就是说他绕过了 if(XXX!=null) 的判定,但是打印出了null。原来是接口的原因! 就是如果你存储了接口的索引,但是你继承这个接口的实体被销毁的时候,你这个接口的索引还在,不会为空,是变成带双引号的null,打印出来自然也是null。
也就是说, if(XXX!=null) 这种判空 对于接口 不是那么可靠
如果之后我继续调用这个接口的函数,就会报空。
搞了这么多年unity这种事情还是才知道,还是才疏学浅,惭愧惭愧呀~

猜你喜欢

转载自blog.csdn.net/qq_27275225/article/details/141598864