[Unity3D]调用函数时出现NullReferenceException的一个可能原因

今天遇到一个报错,如下图:

NullReferenceException 
UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine) (at C:/BuildAgent/work/d3d49558e4d408f4/artifacts/EditorGenerated/UnityEngineMonoBehaviour.cs:63) 
...

使用场景是这样的:

我在一个脚本中需要调用一个协程,该协程被我封装在一个新的脚本里(MyCoroutines.cs),该脚本是一个单例:

 
  
  1. // MyCoroutines.cs
  2. class MyCoroutines : MonoBehavior {
  3. public static MyCoroutines Instance {
  4. get {return sInstance ?? new MyCoroutines() : sInstance;}
  5. }
  6. private MyCoroutines sInstance;
  7. public IEnumerator MethodA() {
  8. // ...
  9. }
  10. }

是这样调用的:

 
  
  1. ...
  2. // 在此处调用
  3. StartCoroutine(MyCoroutines.Instance.MethodA());
  4. ...

然后运行,程序报错。 
百思不得其解。 
明明是个单例,不应该出现NullReference。

网上查了一下,才恍然,其实是我对Unity的运行机制没有理解,先看看别人的解释:

Well, i just guess that the script (and / or the gameobject it's attached to) has been destroyed, or you created the Wait-instance with "new" which would result in the same behaviour since a MonoBehaviour can't live on it's own.

这段就解释的很清楚了:使用new创建实例或该脚本依附的gameObject被销毁了,都会报这种错误,因为MonoBehaviour脚本不能独自存在,必须依附于某个GameObject

所以解决的办法就是让这个脚本依附于某个GameObject就可以了,于是我修改了MyCoroutines类的实现:

 
  
  1. public class MyCoroutines : MonoBehaviour {
  2. private MyCoroutines() { }
  3. static public MyCoroutines GetOrCreate(GameObject gameObject) {
  4. if (gameObject == null) { return new MyCoroutines(); }
  5. var existed = gameObject.GetComponent <MyCoroutines>();
  6. return existed ?? gameObject.AddComponent <MyCoroutines>();
  7. }
  8. }
  9. // 调用:
  10. StartCoroutine(MyCoroutines.GetOrCreate(gameObject).MethodA());

这可能不是最好的解决办法,不过可以正常运行了。

下面是更详细的解释,有兴趣的童鞋可以继续看:

In C# / .NET / Mono instances actually can't be destroyed since they live in a managed memory environment. Objects are destroyed when all references to the object are gone, no longer valid. After that the garbage collector eventually kicks in and removes the object.

However in Unity, since it's core is written in C++, (native) objects can be destroyed on command (with Destroy to be more precise). The Destroy method actually only destroys the object on the c++ side. The managed representation of the object (your MonoBehaviour script) will still be there since the GC can only collect the object when there are no references anymore. That's why Unity actually "fakes" that the reference is null when the object has lost it's native counterpart.

If you use the "new" keyword to create an instance of a MonoBehaviour derived class the instance doesn't have a native counterpart and will always pretend to be null. If you want to create an instance at runtime it has to be attached to a gameobject. This is done with AddComponent

原文地址:http://answers.unity3d.com/questions/745685/nullreferenceexception-on-startcoroutine.html

你可能感兴趣的:(Unity)