# 单例模式

属于创建型模式,是常用的设计模式之一。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
  • 适用性:场景中存在唯一的对象,需要频繁的使用

CSharp 类与 Unity 脚本之间是有差别的,接下来将分开介绍

# CSharp 类

# 懒汉式,线程不安全

  • 这种方式是最基本的实现方式
  • 按需加载 / 懒初始化:只有在调用时才会初始化
  • 缺点:多线程同时访问时,可能会创建多个对象
public class Singleton
{
    /// <summary>
    /// 不允许在类的外部创建对象
    /// </summary>
    private Singleton() { }
    /// <summary>
    /// 入口
    /// </summary>
    private static Singleton? instance;
    /// <summary>
    /// 入口
    /// </summary>
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

# 懒汉式,线程安全,双检锁 / 双重校验锁(double-checked locking)

  • 利用线程锁和两次检查,可以完美的解决懒汉式的缺点
public class Singleton
{
    private Singleton() { }
    private static object Locker = new object();
    private static Singleton? instance;
    public static Singleton Instance
    {
        get
        {
            // 避免每次都使用线程锁
            if (instance == null)
            {
                // 排队创建对象
                lock (Locker)
                {
                    // 排队创建对象时,只有第一次可以创建成功
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

# 饿汉式

  • 一般情况下,建议使用饿汉式
  • 早初始化:在类被加载的时候就初始化
  • 过早初始化会浪费内存
  • 多线程安全
public class Singleton
{
    private Singleton() { }
    private static Singleton instance = new Singleton();
    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

# 泛型抽象类,以饿汉式为例

  • 将单例类的共同的部分封装到父类
  • 父类利用反射创建子类
public abstract class Singleton_T<T> where T : Singleton_T<T>
{
    protected Singleton_T() { }
    private static T instance = Activator.CreateInstance(typeof(T)) as T;
    public static T Instance
    {
        get
        {
            return instance;
        }
    }
}

# Unity 脚本

# 分析

  • 脚本挂载到物体上就会生成一个对象,所以不要把脚本挂载到多个物体上。如果有必要,可以做生成数量的检测(本文没有做这样的检测)
  • 为保证单例模式的通用性,使用泛型类,让子类继承
  • 声明静态字段和静态属性,给外界提供属性。
  • 按需加载(懒汉式),根据传入的子类类型,返回对应的对象,如果对象不存在,则自行创建一个。
    • 如果脚本已经挂载到物体上,就可以直接在 Awake () 中对静态字段赋值
    • 如果脚本没有挂载到物体上,需要新创建一个物体,创建物体会立即执行 Awake (),静态字段会被赋值
  • 如果其他类的 Awake () 的执行时机先于这个类的 Awake (),并且在 Awake () 中调用了这个属性,此时静态字段为空,需要使用 FindObjectOfType<T>()(这是 Unity 的 API)找到这个类
  • 注:如果需要让辅助线程调用单例类,则必须把单例类的脚本挂载到物体上,并且辅助线程不能在 Awake () 之前调用单例类。否则,辅助线程将会调用 Unity 的 API,但是辅助线程不能调用 Unity 的 API(这是 Unity 的规定)

# 使用

  • 继承时必须传递子类类型。
  • 在任意脚本生命周期中,通过子类类型访问静态属性
  • 为了保证正确的脚本执行顺序,使用自定义的方法代替子类中的 Awake ()

完整代码:

MonoSingleton
using UnityEngine;
namespace Common
{
    /// <summary>
    /// 单例模式
    /// 脚本单例类
    /// </summary>
    public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
    {
        //T 表示子类类型
        /// <summary>
        /// 入口
        /// </summary>
        private static T instance;
        /// <summary>
        /// 入口
        /// </summary>
        public static T Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<T>();
                    if (instance == null)
                    {
                        // 创建物体会立即执行 Awake ()
                        new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
                    }
                    else
                    {
                        instance.Init();
                    }
                }
                return instance;
            }
        }
        private void Awake()
        {
            // 如果脚本挂在了物体上,就直接赋值,这样就不用执行 FindObjectOfType<T>() 了
            if (instance == null)
            {
                instance = this as T;
                Init();
            }
        }
        /// <summary>
        /// 以此代替子类中的 Awake ()
        /// </summary>
        protected virtual void Init() { }
    }
}
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Maikire 微信支付

微信支付

Maikire 支付宝

支付宝

Maikire 贝宝

贝宝