MonoSingleton——Unity中的单例模式

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当中,那么上面那段代码就能够改为这样。ui

    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的类,那么上面那段代码就能够改为下面这样:this

    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组件。那么我可不能够直接挂在物体上,那该怎么办?若是我挂多了该怎么办?spa

有办法,这里咱们利用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> : MonoBehaviour
where 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();
    }
}
相关文章
相关标签/搜索