Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)

这篇具有很好参考价值的文章主要介绍了Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Unity下三种单例模式

单例的使用场景

​ 对象全局唯一,且经常被使用。

static静态字段介绍

  1. 所有对象共享static属性,即static属性在此类中是唯一的。
  2. static属性不会被GC回收,随着程序开始而创建,随着程序结束而销毁(so 不要滥用单例哦)

学过面向对象的小伙伴对static想必都不陌生,其具有的特质和今天要讲解的单例十分相似,自然后续的单例模式也会使用到。

刨根问底:static属性为何类中唯一共享?

​ C#中创建的所有类都会存在一个全局唯一的类型对象(System.Type),类型对象中会保存此类的函数表,静态字段等等,也就是说其实静态字段存储在全局唯一对应的类型对象中,而不是存在于此类new出来的实例对象中,现在就能很好的解释静态字段两点性质啦。

普通C#类—饿汉式

为了更好的实现代码复用,以下三种单例模式均会采用工具类的设计方式,即设计成通用的父类,想要实现单例模式的子类只需要继承相应的单例工具类即可!

namespace Common
{
    /// <summary>
    /// 饿汉式单例模式通用父类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Singleton<T> where T : Singleton<T> //添加泛型约束为T必须为其本身或子类
    {
        //防止在外部创建对象,破坏唯一性
        protected Singleton() { }
        public static T Instance { get; private set; }

        //静态构造函数,程序启动时调用
        static Singleton()
        {
            Instance = Activator.CreateInstance(typeof(T), true) as T;
            Instance.Init(); //初始化一次
        }

        /// <summary>
        /// 可选初始化函数
        /// </summary>
        protected virtual void Init()
        {

        }
    }
}
  • 提供虚函数Init,可以通过重写此Init进行类的初始化工作,无需使用构造函数防止多次调用。
  • 利用泛型延迟声明单例模式对象,子类通过继承此父类并将自身类型赋给单例即可轻松实现单例模式,where针对泛型T约束其必须为自身或子类。
  • 饿汉式的单例,即在程序开始时即将单例的static属性Instance进行初始化,饿汉式的两个特征
    • 饿汉式单例是线程安全的,static构造函数只可能运行一次
    • 饿汉式单例存在空引用的风险,如果在另一个类的static构造函数中引用了此单例,由于运行顺序关系可能还没执行到此单例即没实例化,就会报空引用错误。这一点在MonoBehavior脚本的单例中体现尤为明显!

使用方式举个栗子

using Common; //注意命名空间的引用
public class Test : Singleton<Test>
{
	private string str;
	protected override void Init()
	{
		base.Init();
		str = "Hello World";
	}
	
	public string SayHello()
	{
		return str;
	}
}


public class TestMono : MonoBehaviour
{
	private void Awake()
	{
		string str = Test.Instance.SayHello();
		Debug.Log(str);
	}
}

普通C#类—懒汉式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>
    /// 单例类懒加载
    /// </summary>
    public class SingletonLazy<T> where T:SingletonLazy<T>
    {

        private volatile static T instance;
        /*volatile修饰:编译器在编译代码的时候会对代码的顺序进行微调,用volatile修饰保证了严格意义的顺序。
        一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
		精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。*/

        private static object locker = new object();
        public static T Instance { get
            {
                //双重检查 避免多次访问线程锁和多线程访问冲突问题
                if(instance == null)
                {
                    lock(locker)
                    {
                        if(instance==null)
                        {
                            instance = Activator.CreateInstance(typeof(T), true) as T;
                            instance.Init(); //保证只调用一次
                        }
                    }
                }
                return instance;

            } }

        /// <summary>
        /// 可选初始化函数
        /// </summary>
        public virtual void Init()
        {

        }
    }
}

  • 懒汉式单例,即按需加载,第一次使用此对象时加载,懒汉式单例也存在一些特征
    • 懒汉式单例是非线程安全的,可能同一时间有多个调用,会违反单例全局唯一的特性。
      • 多线程冲突问题,自然想到使用锁去隔绝,保证同时只能有一个线程进行实例化,为了让第一个线程实例化后,后续被锁隔离的线程进入时不重复实例化需要再锁的内部进行一重检查判空。
      • 由于锁的性能消耗,当第一次实例化后,后续的调用请求无需再被锁阻塞后再判空,可以在锁外添加第二重检查判空。
    • volatile特性的为了防止极小概率发生的地址问题,大多情况下都可以忽略,如果要追求极致严谨还需要添加此属性,原因在注释中已经解释。

脚本类 — 饿汉 + 懒汉

namespace Common
{
    ///<summary>
    ///脚本单例类,负责为唯一脚本创建实例
    ///<summary>

    public class MonoSingleton<T> : MonoBehaviour where T:MonoSingleton<T> //注意此约束为T必须为其本身或子类
    {
        private static T instance; //创建私有对象记录取值,可只赋值一次避免多次赋值

        public static T Instance
        {
            //实现按需加载
            get
            {
                //当已经赋值,则直接返回即可
                if (instance != null) return instance;

                instance = FindObjectOfType<T>();

                //为了防止脚本还未挂到物体上,找不到的异常情况,可以自行创建空物体挂上去
                if (instance == null)
                {
                    //如果创建对象,则会在创建时调用其身上脚本的Awake即调用T的Awake(T的Awake实际上是继承的父类的)
                    //所以此时无需为instance赋值,其会在Awake中赋值,自然也会初始化所以无需init()
                    new GameObject("Singleton of "+typeof(T)).AddComponent<T>();
                }
                else instance.Init(); //保证Init只执行一次

                return instance;

            }
        }

        private void Awake()
        {
            //若无其它脚本在Awake中调用此实例,则可在Awake中自行初始化instance
            instance = this as T;
            //初始化
            Init();
        }

        //子类对成员进行初始化如果放在Awake里仍会出现Null问题所以自行制作一个init函数解决(可用可不用)
        protected virtual void Init()
        {

        }
    }

}
  • Mono脚本的单例相较于普通C#单例的一些变化点
    • Mono脚本的实例化方式不能靠new,而是要挂载到GameObject身上,如果在场景中预先挂载好则直接获取,如果未挂载则自动创建GameObject并挂载上去。
    • 提供按需加载和初始加载两种方式,按需加载仍在get中进行,而初始加载则不能在构造函数中执行了,而是在脚本生命周期的Awake函数中初始化。
    • 在自动创建物体的地方可能有人会疑惑instance赋值的问题,实际上创建GameObject后的AddComponent就会执行一次Awake如果再赋值Instance或Init就违反了单例的特性。
  • 使用此完善的Mono单例父类,同时实现饿汉和懒汉,无需担心因Awake等调用顺序造成的空指针异常,大胆的使用即可!

结尾

​ 单例模式是常用的工具类,有了这三个脚本即可在开发中遇到单例需求,直接继承即可,工具类可以大大提高开发的速度,且无需做很多重复的工作,后续笔者会逐步将自己开发过程中用到的工具类分享出来哒!文章来源地址https://www.toymoban.com/news/detail-414023.html

到了这里,关于Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 单例模式(懒汉式和饿汉式)

    单例模式是指保证某个类在整个软件系统中只有一个对象实例,并且该类仅提供一个返回其对象实例,由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。 在计算

    2024年02月13日
    浏览(50)
  • JavaEE:单例模式(饿汉模式和懒汉模式)精讲

    前言 什么是单例模式? 其实用通俗的话就是程序猿约定俗成的一些东西,就比如如果你继承了一个抽象类,你就要重写里面的抽象方法,如果你实现了一个接口,你就要重写里面的方法。如果不进行重写,那么编译器就会报错。这其实就是一个规范。 而单例模式能保证某个

    2024年02月04日
    浏览(48)
  • 自学设计模式(类图、设计原则、单例模式 - 饿汉/懒汉)

    设计模式需要用到面向对象的三大特性——封装、继承、多态(同名函数具有不同的状态) UML类图 eg.—— 描述类之间的关系(设计程序之间画类图)  +: public; #: protected; -: private; 下划线: static 属性名:类型(=默认值) 方法和变量分开------- 虚函数斜体,纯虚函数在虚函数类

    2024年02月11日
    浏览(40)
  • 【多线程案例】单例模式(懒汉模式和饿汉模式)

    提起单例模式,就必须介绍设计模式,而设计模式就是在软件设计中,针对特殊问题提出的解决方案。它是多年来针对一些常见的问题的解决方法,具有良好的可复用性、可扩展性和可维护性。 标准的设计模式有23种,单例模式就是最常见的一种,其目的是确认一个类只有一

    2024年02月10日
    浏览(45)
  • 懒汉单例设计模式与饿汉单例设计模式

    单例模式即一个类确保只有一个对象,主要用于避免浪费内存 1 .饿汉单例设计模式 :拿到对象时,对象就早已经创建好了 写法: 把类的构造器私有 在类中自己创建一个对象,并赋值到一个变量 定义一个静态方法,返回自己创建的这个对象 2. 懒汉单例设计模式 :第一次拿到对象时

    2024年02月21日
    浏览(59)
  • 单例模式类设计|什么是饿汉模式和懒汉模式

    那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量干货博客汇总 https://blog.csdn.net/yu_cblog/category_12379430.html?spm=1001.2014.3001.5482 一个类只能创建一个对象,

    2024年02月16日
    浏览(43)
  • 24种设计模式之单例模式(饿汉式、懒汉式)

    单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的ServletContext 、ServletContextConfig 等、Spring框架应用中的。 特点:构造方

    2024年02月07日
    浏览(47)
  • 【单例模式】饿汉式、懒汉式、静态内部类--简单例子

    单例模式是⼀个单例类在任何情况下都只存在⼀个实例,构造⽅法必须是私有的、由⾃⼰创建⼀个静态变量存储实例,对外提供⼀个静态公有⽅法获取实例。 目录 一、单例模式 饿汉式  静态内部类 懒汉式 反射可以破坏单例 道高一尺魔高一丈 枚举 类⼀加载就创建对象,这

    2024年04月23日
    浏览(64)
  • C/C++学习——单例模式(懒汉模式与饿汉模式)

    单例模式 (Singleton Pattern)是一种创建型设计模式,用于确保在应用程序的生命周期内 只存在一个特定类的唯一实例 。这意味着无论何时请求该类的实例,都将返回同一个实例,而不会创建多个相同的对象。 资源共享 :当多个对象需要共享同一个资源,如数据库连接、文件

    2024年02月10日
    浏览(41)
  • 关于Java中单例模式(饿汉模式和懒汉模式)的简析

    目录 一.什么是单例模式 二.饿汉模式和懒汉模式 饿汉模式 代码 懒汉模式 代码 关于多线程安全的问题 如何解决懒汉模式多线程安全问题 双if判断 简单来说,就是我们在程序中通过代码进行限制,在该程序中 只能创建一个对象 因为只能创建一个对象,所以对于我们而言就有两种

    2024年02月10日
    浏览(80)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包