高階 Unity Singleton
此示例將 Internet 上的 MonoBehaviour 單例的多個變體組合成一個,並允許你根據全域性靜態欄位更改其行為。
此示例使用 Unity 5 進行測試。要使用此單例,你需要做的就是按如下方式擴充套件它:public class MySingleton : Singleton<MySingleton> {}
。你可能還需要覆蓋 AwakeSingleton
來使用它而不是通常的 Awake
。要進一步調整,請更改靜態欄位的預設值,如下所述。
- 此實現使用 DisallowMultipleComponent 屬性為每個 GameObject 保留一個例項。
- 這個類是抽象的,只能擴充套件。它還包含一個需要重寫的虛擬方法
AwakeSingleton
,而不是實現普通的Awake
。 - 此實現是執行緒安全的。
- 這個單例已經過優化。通過使用
instantiated
標誌而不是例項 null 檢查,我們避免了 Unity 實現==
運算子所帶來的開銷。 ( 瞭解更多 ) - 當它即將被 Unity 銷燬時,此實現不允許對單例例項的任何呼叫。
- 這個單例有以下選擇:
FindInactive
:是否查詢附加到非活動 GameObject 的相同型別元件的其他例項。Persist
:是否在場景之間保持元件存活。DestroyOthers
:是否要銷燬同型別的任何其他元件並且只保留一個。Lazy
:是否動態設定單例例項(在Awake
中)或僅按需設定(當呼叫 getter 時)。
using UnityEngine;
[DisallowMultipleComponent]
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static volatile T instance;
// thread safety
private static object _lock = new object();
public static bool FindInactive = true;
// Whether or not this object should persist when loading new scenes. Should be set in Init().
public static bool Persist;
// Whether or not destory other singleton instances if any. Should be set in Init().
public static bool DestroyOthers = true;
// instead of heavy comparision (instance != null)
// http://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
private static bool instantiated;
private static bool applicationIsQuitting;
public static bool Lazy;
public static T Instance
{
get
{
if (applicationIsQuitting)
{
Debug.LogWarningFormat("[Singleton] Instance '{0}' already destroyed on application quit. Won't create again - returning null.", typeof(T));
return null;
}
lock (_lock)
{
if (!instantiated)
{
Object[] objects;
if (FindInactive) { objects = Resources.FindObjectsOfTypeAll(typeof(T)); }
else { objects = FindObjectsOfType(typeof(T)); }
if (objects == null || objects.Length < 1)
{
GameObject singleton = new GameObject();
singleton.name = string.Format("{0} [Singleton]", typeof(T));
Instance = singleton.AddComponent<T>();
Debug.LogWarningFormat("[Singleton] An Instance of '{0}' is needed in the scene, so '{1}' was created{2}", typeof(T), singleton.name, Persist ? " with DontDestoryOnLoad." : ".");
}
else if (objects.Length >= 1)
{
Instance = objects[0] as T;
if (objects.Length > 1)
{
Debug.LogWarningFormat("[Singleton] {0} instances of '{1}'!", objects.Length, typeof(T));
if (DestroyOthers)
{
for (int i = 1; i < objects.Length; i++)
{
Debug.LogWarningFormat("[Singleton] Deleting extra '{0}' instance attached to '{1}'", typeof(T), objects[i].name);
Destroy(objects[i]);
}
}
}
return instance;
}
}
return instance;
}
}
protected set
{
instance = value;
instantiated = true;
instance.AwakeSingleton();
if (Persist) { DontDestroyOnLoad(instance.gameObject); }
}
}
// if Lazy = false and gameObject is active this will set instance
// unless instance was called by another Awake method
private void Awake()
{
if (Lazy) { return; }
lock (_lock)
{
if (!instantiated)
{
Instance = this as T;
}
else if (DestroyOthers && Instance.GetInstanceID() != GetInstanceID())
{
Debug.LogWarningFormat("[Singleton] Deleting extra '{0}' instance attached to '{1}'", typeof(T), name);
Destroy(this);
}
}
}
// this might be called for inactive singletons before Awake if FindInactive = true
protected virtual void AwakeSingleton() {}
protected virtual void OnDestroy()
{
applicationIsQuitting = true;
instantiated = false;
}
}