为什么我的单身人士有两个不同的实例?

时间:2015-05-13 04:25:51

标签: c# unity3d singleton monodevelop

我使用以下模式在Unity中制作我的单身人士

public class BlobManager : MonoBehaviour  
{
    public static BlobManager instance {get; private set;}
    void Awake () 
    {
        if(instance != null && instance != this)
           Destroy(gameObject);

        instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void SomeFunction()
    {
       if (this != instance)
           Debug.Log("They're Different!")
    }
}

SomeFunction()所示,它们总是不同。当我为BlobManager类本地设置一个值时,另一个用于使用静态var“instance”,如下所示:

foo = "bar"; 
BlobManager.instance.foo = "foo";

在类中的断点处调试器中看到的foo的值将始终为“bar”。但是当其他类尝试访问同一个变量时,它将是“foo”。

我不确定如何查看Monodevelop中的内存地址,但我确定thisthis.instance会有不同的内存地址。 我的单身人士模式有问题吗?我也试过其他模式也有同样的结果。

5 个答案:

答案 0 :(得分:1)

在您的示例中,BlobManager实例化多次出现,但只有第一个实例保存在属性Instance中。

MonoBehaviour派生意味着您可以将脚本附加到许多实例上。 Singleton必须具有私有构造函数,没有MonoBehaviour派生,并且您不必将此脚本附加到任何对象。如果需要将脚本附加到对象,则应创建另一个派生自MonoBehaviour并控制单例的脚本

示例(未测试):

public class BlobManager
{
    static BlobManager _inst;
    public static BlobManager Instance {
    get
    {
        if (_inst == null)
            _inst = new BlobManager();
        return _inst;
    }
    }

    private BlobManager() 
    {

    }
}

答案 1 :(得分:1)

这是一个非常完善的Singleton模式,可以轻松地重复使用monobehaviours

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance
    {
        get
        {
            if(mInstance == null && !isQuitting)
            {
                mInstance = FindObjectOfType(typeof(T)) as T;
                if(mInstance == null)
                {
                    GameObject go = new GameObject(typeof(T).Name);
                    mInstance = go.AddComponent<T>();
                    Logger.LogWarning("Creating " + go.name + " since one does not exist in the scene currently");
                }
            }
            return mInstance;
        }
    }

    public static bool HasInstance
    {
        get
        {
            //if we dont have one we try to find one
            if(mInstance == null)
            {
                mInstance = FindObjectOfType(typeof(T)) as T;
            }
            return mInstance != null;
        }
    }

    static T mInstance;

    protected static bool isQuitting = false;
    protected virtual void OnApplicationQuit()
    {
        isQuitting = true;
    }

}

你的单例模式失败的原因是因为在你能够通过Singleton接口访问它之前不能保证调用Awake,尽管文档声称总是首先调用Awake。

以上允许你在任何时候引用单例并且它将始终返回一个有效的实例,除非游戏正在退出,在这种情况下,创建一个新实例将抛出未清除的错误并在场景中留下一个额外的实例停止后。

答案 2 :(得分:0)

我建议不要尝试使用Monobehaviour的经典Singleton模式。相反,也许有一个容器对象用于这些类型的东西,比如一个空的游戏对象,并检索所需的行为,如下所示:

GameObject.Find("ContainerName").GetComponent<BlobManager>();

这可以像Singleton模式一样全局访问。您也可以手动将这一个实例放在容器对象上。这也允许编辑检查员的细节。

答案 3 :(得分:0)

Unity中最好的方法是避免多次创建 BlobManager 。例如,您可以使用特殊引导场景来实现此目的,该场景负责设置与场景无关的对象,即具有应用范围。如果初始化完成,则该场景永远不会再次加载,而是它。

Awake的实现问题是instance始终设置为当前实例化的BlobManager组件,无论是否已存在。这是因为Destroy不会执行return,无论如何都会执行之后的代码。更糟糕的是,它没有立即执行。这就是说instance将始终使用最新的组件进行更新,即使它将在帧的末尾被销毁,然后将被设置为null。

我建议您修改Awake方法:

void Awake () 
{
    if(instance == null) {
        instance = this;
        DontDestroyOnLoad(gameObject);
    } else {
        Destroy(gameObject);
    }
}

请注意,如果单调使用它们很有用。拥有一堆被认为是不好的做法,可能会让你的项目有一天成为一场噩梦(s Links in Wikipedia更多关于此事。)

要至少将它们附加到一个主要游戏对象,您可以使用此答案中描述的方法:Unity singleton manager classes

答案 4 :(得分:0)

单例模式需要私有构造函数,否则它不是单例

默认情况下,一个类有一个公共默认构造函数,所以为了避免这种行为,我们通过私有默认构造函数来破坏它