MonoSingleton——Unity中的单例模式

来源:互联网 发布:淘宝页头图片怎么修改 编辑:程序博客网 时间:2024/06/01 10:43

Unity中有很多特别的类需要以单例模式呈现,比如全局的UI管理类,各种缓存池,以及新手导航类等等。而Unity中,因为所有继承自Monobehaviour的脚本在实现的时候都是单线程的,所以像网上流传的一些C#的实现方式就显得不那么的实用了。

很多国内的公司所使用的MonoSingleton都是有问题的,比如像Easytouch中关于单例是这样实现中有这样一段代码。

public static T instance{    get    {        if (m_Instance == null)        {            m_Instance = GameObject.FindObjectOfType(typeof(T)) as T;//1这里耗费性能,有风险            if (m_Instance == null)//2            {                m_Instance = new GameObject("Singleton of " + typeof(T).ToString(), typeof(T)).GetComponent<T>();                m_Instance.Init();            }        }        return m_Instance;    }}

那么我标注的两处就是代码当中不正确的地方。2处这是明显的套用了多线程的单例实现方式,而实际上,在单线程模式当中这个判断并没有意义。而1中,直接对全场景进行搜索的过程其本身就很浪费性能。那么正确的实现方式是什么呢?

首先,我们需要一个全局变量,比如,先建立一个全局类Global

public abstract class Global : MonoBehaviour{    public static HashSet<string> Singleton=new HashSet<string>();}

每次建立都将类名存进HashSet当中,那么上面那段代码就可以改成这样。

public static T instance{    get    {        if (m_Instance == null)        {            var name = "Singleton of " + typeof(T).ToString();            var flag = Global.Singleton.Contains(name);            if (!flag)            {                m_Instance = new GameObject(name, typeof(T)).GetComponent<T>();                m_Instance.Init();                Global.Singleton.Add(name);            }        }        return m_Instance;    }}
可能您要说了,我已经有了一个全局类了,那么难道还要再填一个东西?我只想直接用,用没有更简便的方法。您要说更好,不一定,但是更简便,确实有的。我们这里可以用上互斥类Mutex的类,那么上面那段代码就可以改成下面这样:

public static T instance{    get    {        if (m_Instance == null)//注意,此处在实际中只执行一次。        {            bool createdNew;            var name = "Singleton of " + typeof(T).ToString();            Mutex mutex = new Mutex(false, name, out createdNew);            if (createdNew)            {                m_Instance = new GameObject(name, typeof(T)).GetComponent<T>();                m_Instance.Init();            }        }        return m_Instance;    }}

但这只是说,我如果在其他地方操作这个单例,而这个单例还必须新建一个游戏物体,还必须挂在上面,挂在游戏物体上就至少要有一个transform组件。那么我可不可以直接挂在物体上,那该怎么办?如果我挂多了该怎么办?

有办法,这里我们利用Awake()方法

private void Awake(){    if (m_Instance == null)    {        bool createdNew;        var name = "Singleton of " + typeof(T).ToString();        Mutex mutex = new Mutex(false, name, out createdNew);        if (createdNew)        {            m_Instance = this as T;            m_Instance.Init();        }    }    else    {        Destroy(this);    }

这样就可以保证运行时的单例了。那么完整的MonoSingleton还需要一些细节。比如在我的单例基类中,我设计成抽象类,提供了两个抽象函数,分别是初始化和逆初始化。之所以这么做就是为了在我们继承MonoSingleton的时候想一想,是不是必须要把这个类做成单例的,它一定是有单例的必要所以才是单例的,而不是将单例当静态使用。

using UnityEngine;using System.Collections;using System.Threading;/// <summary>/// 单例基类,提供两个抽象函数Init 和 DisInit 初始化和逆初始化过程。/// </summary>/// <typeparam name="T"></typeparam>public abstract class MonoSingleton<T> : MonoBehaviourwhere T : MonoSingleton<T>{    private static T m_Instance = null;    private static string name;    private static Mutex mutex;    public static T instance    {        get        {            if (m_Instance == null)            {                if ( IsSingle())                {                    m_Instance = new GameObject(name, typeof(T)).GetComponent<T>();                    m_Instance.Init();                }            }            return m_Instance;        }    }    private static bool IsSingle()    {        bool createdNew;        name = "Singleton of " + typeof(T).ToString();        mutex = new Mutex(false, name, out createdNew);        return createdNew;    }    private void Awake()    {        if (m_Instance == null)        {            if (IsSingle())            {                m_Instance = this as T;                m_Instance.Init();            }        }        else        {            Destroy(this);        }    }    protected abstract void Init();    protected abstract void DisInit();    private void OnDestory()    {        if (m_Instance!=null)        {            mutex.ReleaseMutex();            DisInit();            m_Instance = null;        }    }    private void OnApplicationQuit()    {        mutex.ReleaseMutex();    }}