当前位置:网站首页>Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)
Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)
2022-07-07 13:32:00 【窗外听轩雨】
Unity下三种单例模式
单例的使用场景
对象全局唯一,且经常被使用。
static静态字段介绍
- 所有对象共享static属性,即static属性在此类中是唯一的。
- 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等调用顺序造成的空指针异常,大胆的使用即可!
结尾
单例模式是常用的工具类,有了这三个脚本即可在开发中遇到单例需求,直接继承即可,工具类可以大大提高开发的速度,且无需做很多重复的工作,后续笔者会逐步将自己开发过程中用到的工具类分享出来哒!
边栏推荐
- The significance of XOR in embedded C language
- Iterator and for of.. loop
- webgl_ Graphic transformation (rotation, translation, zoom)
- Three. JS introduction learning notes 12: the model moves along any trajectory line
- How to release NFT in batches in opensea (rinkeby test network)
- 一大波开源小抄来袭
- Summary of knowledge points of xlua hot update solution
- C Alibaba cloud OSS file upload, download and other operations (unity is available)
- Jacobo code coverage
- Database exception resolution caused by large table delete data deletion
猜你喜欢
【花雕体验】15 尝试搭建Beetle ESP32 C3之Arduino开发环境
Numpy --- basic learning notes
Three. JS introductory learning notes 11:three JS group composite object
2022第四届中国(济南)国际智慧养老产业展览会,山东老博会
After UE4 is packaged, mesh has no material problem
Annexb and avcc are two methods of data segmentation in decoding
numpy--疫情数据分析案例
When opening the system window under UE4 shipping, the problem of crash is attached with the plug-in download address
2022 all open source enterprise card issuing network repair short website and other bugs_ 2022 enterprise level multi merchant card issuing platform source code
神经网络c语言中的指针是怎么回事
随机推荐
【数字IC验证快速入门】26、SystemVerilog项目实践之AHB-SRAMC(6)(APB协议基本要点)
Virtual memory, physical memory /ram what
leetcode 241. Different ways to add parentheses design priority for operational expressions (medium)
Getting started with webgl (4)
有钱人买房就是不一样
Three. JS introductory learning notes 11:three JS group composite object
Apache Doris刚“毕业”:为什么应关注这种SQL数据仓库?
[quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)
C4D learning notes 3- animation - animation rendering process case
[quick start of Digital IC Verification] 29. Ahb-sramc (9) (ahb-sramc svtb overview) of SystemVerilog project practice
航运船公司人工智能AI产品成熟化标准化规模应用,全球港航人工智能/集装箱人工智能领军者CIMC中集飞瞳,打造国际航运智能化标杆
numpy--疫情数据分析案例
【数字IC验证快速入门】23、SystemVerilog项目实践之AHB-SRAMC(3)(AHB协议基本要点)
[quick start of Digital IC Verification] 22. Ahb-sramc of SystemVerilog project practice (2) (Introduction to AMBA bus)
C4D learning notes 1- animation - animation key frames
Async and await
Runnable是否可以中断
[quick start of Digital IC Verification] 18. Basic grammar of SystemVerilog learning 5 (concurrent threads... Including practical exercises)
2022山东智慧养老展,适老穿戴设备展,养老展,山东老博会
15. Using the text editing tool VIM