SingletonKit单例源码阅读学习

singletonkit · 浏览次数 : 0

小编点评

本文主要介绍了在Unity游戏开发中如何使用QFramwork框架实现单例模式。文章首先解释了单例模式的基本概念,然后详细分析了在Unity中如何使用MonoSingleton和SingletonProperty这两个工具类来实现单例。最后,文章还提供了一个简单的案例来展示如何使用这些工具类。 1. **单例模式介绍**:文章首先介绍了单例模式的基本概念,指出它是唯一能够访问特定资源的类,避免了资源的重复创建和浪费。 2. **MonoSingleton和SingletonProperty实现**:接着,文章详细分析了如何在Unity中使用MonoSingleton和SingletonProperty这两个工具类来实现单例。MonoSingleton是泛型类,需要继承MonoBehaviour类和ISingleton接口;而SingletonProperty则是属性单例类,只需要继承ISingleton接口。 3. **案例分析**:文章最后提供了一个简单的案例来展示如何使用MonoSingleton和SingletonProperty来实现单例。案例中展示了如何在一个脚本中使用Class2Singleton和Class2MonoSingleton,以及如何在一个脚本中使用Class2SingletonProperty和Class2MonoSingletonProperty。 4. **使用MonoSingletonProperty的好处**:文章还讨论了使用MonoSingletonProperty的好处,包括简化代码、方便继承Mono相关的类,以及作为泛型约束使得想要成为单例的类必须要继承ISingleton接口。 总的来说,文章通过详细的代码分析和案例演示,向读者展示了如何在Unity中有效地实现单例模式,同时也展示了如何使用QFramwork框架提供的MonoSingleton和SingletonProperty工具类来简化这一过程。

正文

阅读学习QFramwork中的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("我是第一种单例使用方式,销毁之后,再次使用噢");
            
        }
    }

image-20240617170633493

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

image-20240617175832663

5s后对单例对象进行释放:

img

使用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,由于C#中只支持class单继承,如果我想要此单例类再继承一个Mono相关的类,那么将增加很多的编码工作。

image-20240617184607191

而使用MonoSingletonProperty之后是继承接口,可以方便继承Mono相关的类。

那么使用ISingleton接口的作用是什么?

作为泛型约束,也就是说想要成为单例的类必须要继承ISingleton接口才可以使用MonoSingletonProperty/SingletonProperty两个工具类。

变相的表明,想要加入单例圈子,需要继承此接口来打个标签。

image-20240617185704194

image-20240617185718773

UML图

image-20240617201339124

源码地址:https://github.com/liangxiegame/SingletonKit

与SingletonKit单例源码阅读学习相似的内容: