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