前言:单例(Singleton)是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。
无论什么语言都会有用到单例的地方,最基础的单例示例如下:
public class Test
{
static protected Test _instance;
static protected bool IsCreate = false;
public static Test Instance
{
get
{
if (IsCreate == false)
{
CreateInstance();
}
return _instance;
}
}
public static void CreateInstance()
{
if (IsCreate == true)
return;
IsCreate = true;
_instance = new Test();
}
public static void ReleaseInstance()
{
_instance = default(Test);
IsCreate = false;
}
}
上面这部分代码就实现了单例,可以直接通过Test.Instance访问类型,当然这么编写后外部还能通过new Test()再次创建Test类的对象,如果不想外部再次创建,那么需要把Test类的构造访问权限改成私有或者受保护的,例如:
protected Test()
{
}
private Test()
{
}
然而如果多个类需要使用单例,我们不会在每个类里面都写一遍单例部分的代码,因此就有了编写通用的单例模板类的原因,那么需要有单例的类,只要继承单例模板类,就拥有了单例类的特性,同样的也会有缺陷,就比如构造函数没办法改成私有或者受保护的,但不妨碍他的使用便利性。
同样的Unity中如果需要挂载在Unity的GameObject上面,那么就需要继承Unity中独有的MonoBehaviour,并且如果改对象切换场景不删除的话还需要调用DontDestroyOnLoad
因此在Unity中单例模板,可以分为三种
1、不继承MonoBehaviour的单例
2、继承MonoBehaviour的单例
3、继承MonoBehaviour并且DontDestroyOnLoad的单例
下面就直接放上Unity中直接可用的三种单例模板
namespace TenderRain.Core
{
public class Singleton<T> where T : new()
{
protected static T _instance;
protected static bool IsCreate = false;
public static T Instance
{
get
{
if (IsCreate == false)
{
CreateInstance();
}
return _instance;
}
}
public static void CreateInstance()
{
if (IsCreate == true)
return;
IsCreate = true;
_instance = new T();
}
public static void ReleaseInstance()
{
_instance = default(T);
IsCreate = false;
}
}
/// <summary>
/// 这样可以避免使用new来构造单例对象。通过DontDestroyOnLoad,gameobject来构造对象,
/// 单例带来可能带来内存泄露问题和线程安全问题
/// </summary>
public class Singleton_DontDestroy<T> : UnityEngine.MonoBehaviour where T : UnityEngine.MonoBehaviour
{
private static T _instance;
private static object _lock = new object();
public static T Instance
{
get
{
if (applicationIsQuitting)
{
UnityEngine.Debug.LogWarning($"[Singleton_DontDestroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} already destroyed on application quit. Won't create again - returning null.");
return null;
}
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
var objects = UnityEngine.Resources.FindObjectsOfTypeAll(typeof(T));
if (objects.Length > 1)
{
_instance = objects[0] as T;
UnityEngine.Debug.LogWarning($"[Singleton_DontDestroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} Something went really wrong there should never be more than 1 singleton! Reopening the scene might fix it.");
return _instance;
}
if (_instance == null)
{
_instance = new UnityEngine.GameObject().AddComponent<T>();
_instance.name = "[Singleton_DontDestroy] " + typeof(T).ToString();
DontDestroyOnLoad(_instance.transform.root.gameObject);
UnityEngine.Debug.Log($"[Singleton_DontDestroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} was created with DontDestroyOnLoad.");
}
else
{
_instance.name = "[Singleton_DontDestroy] " + typeof(T).ToString();
DontDestroyOnLoad(_instance.transform.root.gameObject);
UnityEngine.Debug.Log($"[Singleton_DontDestroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} already existed with DontDestroyOnLoad.");
}
}
}
}
return _instance;
}
}
protected static bool applicationIsQuitting = false;
public static bool IsApplicationQuitting
{
get { return applicationIsQuitting; }
}
protected virtual void Awake()
{
if (_instance == null)
{
_instance = this as T;
DontDestroyOnLoad(_instance.transform.root.gameObject);
}
else if (_instance != this)
Destroy(gameObject);
}
public virtual T CastInstanceType()
{
if (_instance == null)
_instance = this as T;
else if (_instance != this)
_instance = _instance as T;
return _instance;
}
public void SetInstance(T instance, bool isNotNullSet = false)
{
_instance = _instance == null || isNotNullSet ? instance : _instance;
}
protected virtual void OnCreated()
{
}
protected virtual void OnDestory()
{
_instance = null;
applicationIsQuitting = true;
}
protected virtual void OnApplicationQuit()
{
_instance = null;
applicationIsQuitting = true;
}
public static T Create()
{
return Instance;
}
};
public class Singleton_Destory<T> : UnityEngine.MonoBehaviour where T : UnityEngine.MonoBehaviour
{
private static T _instance;
private static object _lock = new object();
public static T Instance
{
get
{
if (applicationIsQuitting)
{
UnityEngine.Debug.LogWarning($"[Singleton_Destroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} already destroyed on application quit. Won't create again - returning null.");
return null;
}
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
var objects = UnityEngine.Resources.FindObjectsOfTypeAll(typeof(T));
if (objects.Length > 1)
{
_instance = objects[0] as T;
UnityEngine.Debug.LogWarning($"[Singleton_Destroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} Something went really wrong there should never be more than 1 singleton! Reopening the scene might fix it.");
return _instance;
}
if (_instance == null)
{
_instance = new UnityEngine.GameObject().AddComponent<T>();
_instance.name = "[Singleton_Destroy] " + typeof(T).ToString();
UnityEngine.Debug.Log($"[Singleton_DontDestroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} was created with DestroyOnLoad.");
}
else
{
_instance.name = "[Singleton_Destroy] " + typeof(T).ToString();
UnityEngine.Debug.Log($"[Singleton_Destroy] Instance Type = {typeof(T)} Name = {_instance.gameObject.name} already existed with DestroyOnLoad.");
}
}
}
}
return _instance;
}
}
protected static bool applicationIsQuitting = false;
public static bool IsApplicationQuitting
{
get { return applicationIsQuitting; }
}
protected virtual void Awake()
{
if (_instance == null)
_instance = this as T;
else if (_instance != this)
Destroy(gameObject);
}
public virtual T CastInstanceType()
{
if (_instance == null)
_instance = this as T;
else if (_instance != this)
_instance = _instance as T;
return _instance;
}
public void SetInstance(T instance, bool isNotNullSet = false)
{
_instance = _instance == null || isNotNullSet ? instance : _instance;
}
protected virtual void OnDestory()
{
_instance = null;
applicationIsQuitting = true;
}
protected virtual void OnApplicationQuit()
{
_instance = null;
applicationIsQuitting = true;
}
public static T Create()
{
return Instance;
}
}
}