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

 

  

  

  

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM