閱讀學習QFramework中的SingletonKit原始碼.
Singleton 普通類的單例
作為最常用的單例模組,透過繼承單例泛型類來實現,需要私有構造;
//使用第一種介面單例方式
internal class Class2Singleton : Singleton<Class2Singleton>
{
//記錄被初始化的次數
private static int mIndex = 0;
//私有構造
private Class2Singleton(){}
public override void OnSingletonInit()
{
mIndex++;
Log(mIndex.ToString());
}
public void Log(string content)
{
Debug.Log("Class2Singleton" + content);
}
}
看一下父類的程式碼內容,首先是ISingleton介面,方便對單例生命週期進行管理,留有給單例中資料進行初始化操作的方法。
/// <summary>
/// 單例介面
/// </summary>
public interface ISingleton
{
/// <summary>
/// 單例初始化(繼承當前介面的類都需要實現該方法)
/// </summary>
void OnSingletonInit();
}
/// <summary>
/// 普通類的單例
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class Singleton<T> : ISingleton where T : Singleton<T>
{
/// <summary>
/// 靜態例項
/// </summary>
protected static T mInstance;
/// <summary>
/// 標籤鎖:確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區。
/// 如果其他執行緒試圖進入鎖定的程式碼,則它將一直等待(即被阻止),直到該物件被釋放
/// </summary>
static object mLock = new object();
/// <summary>
/// 靜態屬性
/// </summary>
public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
//呼叫單例構造器例項化單例物件
mInstance = SingletonCreator.CreateSingleton<T>();
}
}
return mInstance;
}
}
/// <summary>
/// 資源釋放
/// </summary>
public virtual void Dispose()
{
mInstance = null;
}
/// <summary>
/// 單例初始化方法
/// </summary>
public virtual void OnSingletonInit()
{
}
}
其中呼叫單例建構函式中相關內容:
internal static class SingletonCreator
{
//透過反射來進行物件構造
static T CreateNonPublicConstructorObject<T>() where T : class
{
var type = typeof(T);
// 獲取私有建構函式
var constructorInfos = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 獲取無參建構函式
var ctor = Array.Find(constructorInfos, c => c.GetParameters().Length == 0);
if (ctor == null)
{
throw new Exception("Non-Public Constructor() not found! in " + type);
}
return ctor.Invoke(null) as T;
}
public static T CreateSingleton<T>() where T : class, ISingleton
{
var type = typeof(T);
var monoBehaviourType = typeof(MonoBehaviour);
if (monoBehaviourType.IsAssignableFrom(type))
{
return CreateMonoSingleton<T>();
}
else
{
var instance = CreateNonPublicConstructorObject<T>();
instance.OnSingletonInit();
return instance;
}
}
//······
}
編寫案例呼叫單例;
public class SingletonExample : MonoBehaviour
{
private void Start()
{
Class2Singleton.Instance.Log("我是第一種單例使用方式,第一次使用噢!");
Class2Singleton.Instance.Dispose();
//再次呼叫
Class2Singleton.Instance.Log("我是第一種單例使用方式,銷燬之後,再次使用噢");
}
}
MonoSingleton繼承Mono的單例
MonoSingleton是繼承Mono的單例型別,繼承MonoBehaviour類和ISingleton介面,其實現方式與Singleton的內容相似
/// <summary>
/// 靜態類:MonoBehaviour類的單例
/// 泛型類:Where約束表示T型別必須繼承MonoSingleton<T>
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class MonoSingleton<T> : MonoBehaviour, ISingleton where T : MonoSingleton<T>
{
/// <summary>
/// 靜態例項
/// </summary>
protected static T mInstance;
/// <summary>
/// 靜態屬性:封裝相關例項物件
/// </summary>
public static T Instance
{
get
{
if (mInstance == null && !mOnApplicationQuit)
{
mInstance = SingletonCreator.CreateMonoSingleton<T>();
}
return mInstance;
}
}
/// <summary>
/// 實現介面的單例初始化
/// </summary>
public virtual void OnSingletonInit()
{
}
/// <summary>
/// 資源釋放
/// </summary>
public virtual void Dispose()
{
if (SingletonCreator.IsUnitTestMode)
{
var curTrans = transform;
//清除單元測試建立的單例物件
do
{
var parent = curTrans.parent;
DestroyImmediate(curTrans.gameObject);
curTrans = parent;
} while (curTrans != null);
mInstance = null;
}
else
{
Destroy(gameObject);
}
}
/// <summary>
/// 當前應用程式是否結束 標籤
/// </summary>
protected static bool mOnApplicationQuit = false;
/// <summary>
/// 應用程式退出:釋放當前物件並銷燬相關GameObject
/// </summary>
protected virtual void OnApplicationQuit()
{
mOnApplicationQuit = true;
if (mInstance == null) return;
Destroy(mInstance.gameObject);
mInstance = null;
}
/// <summary>
/// 釋放當前物件
/// </summary>
protected virtual void OnDestroy()
{
mInstance = null;
}
/// <summary>
/// 判斷當前應用程式是否退出
/// </summary>
public static bool IsApplicationQuit
{
get { return mOnApplicationQuit; }
}
}
來編寫案例來使用Mono單例
namespace QFramework.Example
{
//使用MonoSingletonExample
internal class Class2MonoSingleton : MonoSingleton<Class2MonoSingleton>
{
public override void OnSingletonInit()
{
Debug.Log(name + "---" + "OnSingletonInit");
}
private void Awake()
{
Debug.Log(name + "---" + "awake");
}
private void Start()
{
Debug.Log(name + "---" + "start");
}
protected override void OnDestroy()
{
base.OnDestroy();
Debug.Log(name + "---" + "OnDestroy");
}
}
public class MonoSingletonExample : MonoBehaviour
{
private IEnumerator Start()
{
var instance = Class2MonoSingleton.Instance;
yield return new WaitForSeconds(5.0f);
instance.Dispose();
}
}
}
5s後對單例物件進行釋放:
使用MonoSingletonProperty/SingletonProperty兩個工具類來封裝單例,在實現設定單例類時候不需要再繼承 MonoSingleton 或 Singleton ,只需要繼承ISingleton介面即可,當然,此處的介面作用就是對該類進行標記說說明,表明類是單例模式。下面看下兩個工具類的具體實現:
/// <summary>
/// 屬性單例類
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SingletonProperty<T> where T : class, ISingleton
{
/// <summary>
/// 靜態例項
/// </summary>
private static T mInstance;
/// <summary>
/// 標籤鎖
/// </summary>
private static readonly object mLock = new object();
/// <summary>
/// 靜態屬性
/// </summary>
public static T Instance
{
get
{
lock (mLock)
{
if (mInstance == null)
{
mInstance = SingletonCreator.CreateSingleton<T>();
}
}
return mInstance;
}
}
/// <summary>
/// 資源釋放
/// </summary>
public static void Dispose()
{
mInstance = null;
}
}
/// <summary>
/// 繼承Mono的屬性單例?
/// </summary>
/// <typeparam name="T"></typeparam>
public static class MonoSingletonProperty<T> where T : MonoBehaviour, ISingleton
{
private static T mInstance;
public static T Instance
{
get
{
if (null == mInstance)
{
mInstance = SingletonCreator.CreateMonoSingleton<T>();
}
return mInstance;
}
}
public static void Dispose()
{
if (SingletonCreator.IsUnitTestMode)
{
UnityEngine.Object.DestroyImmediate(mInstance.gameObject);
}
else
{
UnityEngine.Object.Destroy(mInstance.gameObject);
}
mInstance = null;
}
}
使用SignetonProperty來實現單例類:
internal class Class2SingletonProperty : ISingleton
{
private static int mIndex = 0;
//靜態單例屬性
public static Class2SingletonProperty Instance
{
get { return SingletonProperty<Class2SingletonProperty>.Instance; }
}
public void OnSingletonInit()
{
mIndex++;
}
private Class2SingletonProperty()
{
}
public void Dispose()
{
SingletonProperty<Class2SingletonProperty>.Dispose();
}
public void Log(string content)
{
Debug.Log("Class2SingletonProperty" + mIndex + "---" + content);
}
}
public class SingletonPropertyExample : MonoBehaviour
{
private void Start()
{
Class2SignetonProperty.Instance.Log("Hello Tony!");
Class2SignetonProperty.Instance.Dispose();
Class2SignetonProperty.Instance.Log("Hello TonyChang");
}
}
其執行結果和第一個單例實現案例結果相同。
使用MonoSingletonProperty實現單例類:
internal class Class2MonoSingletonProperty : MonoBehaviour, ISingleton
{
//僅僅需要宣告靜態屬性即可
public static Class2MonoSingletonProperty Instance
{
get { return MonoSingletonProperty<Class2MonoSingletonProperty>.Instance; }
}
//繼承ISingleton 介面實現方法
public void OnSingletonInit()
{
Debug.Log(name + "==" + "OnSingletonInit" );
}
public void Dispose()
{
MonoSingletonProperty<Class2MonoSingletonProperty>.Dispose();
}
private void Awake()
{
Debug.Log(name + "==" + "Awake" );
}
private void Start()
{
Debug.Log(name + "==" + "Start" );
}
private void OnDestroy()
{
Debug.Log(name + "==" + "OnDestroy" );
}
}
public class MonoSingletonPropertyExample:MonoBehaviour
{
private IEnumerator Start()
{
var instance = Class2MonoSingletonProperty.Instance;
yield return new WaitForSeconds(5.0f);
instance.Dispose();
}
}
其執行結果和第二個繼承Mono的單例實現案例結果相同。
相關問題:
那麼不免產生疑問--使用MonoSingletonProperty/SingletonProperty兩個工具類來封裝單例好處是什麼?或者說解決什麼問題。
我們看不使用MonoSingletonProperty實現單例,需要繼承抽象類MonoSingleton
而使用MonoSingletonProperty之後是繼承介面,可以方便繼承Mono相關的類。
那麼使用ISingleton介面的作用是什麼?
作為泛型約束,也就是說想要成為單例的類必須要繼承ISingleton介面才可以使用MonoSingletonProperty/SingletonProperty兩個工具類。
變相的表明,想要加入單例圈子,需要繼承此介面來打個標籤。
UML圖
原始碼地址:https://github.com/liangxiegame/SingletonKit