• 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当中,那么上面那段代码就可以改成这样。

        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> : 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();
        }
    }

      

      

      

      

      

  • 相关阅读:
    Java提高篇(三四)-----fail-fast机制
    opencv提取surf特征点出现的错误
    Android开发_Gson解析
    关于权限管理设计文章整理,希望对大家有所帮助
    URAL
    JAVA编程心得-多态设计初步
    paip. mysql如何临时 暂时 禁用 关闭 触发器
    Citrix服务器虚拟化之三十 XenApp 6.5发布流式应用程序
    在TextView使用部分颜色文字
    微软面试题:求整数随机数构成的数组中找到长度大于=3的最长的等差数列
  • 原文地址:https://www.cnblogs.com/fastcam/p/5924036.html
Copyright © 2020-2023  润新知