在面试过程中被问到用过哪些设计模式的问题应该很常见。而单例模式作为最简单最常用的应该是第一个被想到的。开篇就拿单例模式”开刀”吧。
什么是单例模式?
单例模式就是 用来保证一个类只有一个实例,并向外部提供一个全局访问点。 如:游戏中的对话框,不能每次显示都要创建出来一个,关了就销毁吧。
这也体现出了单例模式的如下优点:全局一份实例,减少内存占用,也减少了资源的频繁创建销毁。

如何写一个单例模式呢?如游戏中的一个XXXManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class XXXManager { private static XXXManager _instence = new XXXManager();
private XXXManager(){}
public static XXXManager Singleton { get{ return _instence; } } }
|
单例模式的分类
饿汉式。如上面的例子,在类加载的时候不管用到用不到就会创建实例。
优点:线程安全。
缺点:比较浪费内存。
懒汉式。只有在调用到的时候才会创建实例。比较节约内存。多线程可能会出问题。
优点:节约内存。
缺点:单线程没问题,多线程时非线程安全(但可以解决)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class XXXManager { private static XXXManager _instence = null; private XXXManager(){} public static XXXManager Singleton { get { if (_instence == null) _instence = new XXXManager();
return _instence; } } }
|
加锁解决懒汉式的非线程安全问题
由于Unity是单线程运行,可以不考虑多线程下非线程安全问题。但是要注意在其他地方使用的时候。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private static object mLock = new object();
public static XXXManager Singleton { get { if (_instence == null) { lock (mLock) { if (_instence == null) _instence = new XXXManager(); } } return _instence; } }
|
每个单例都要重新写一遍,好麻烦?
一个工程中可能有很多类都要用到单例,总不能每次都重新写一遍吧,这就导致有很多重复的代码。可以使用泛型 将单例抽象出来。
但是_instence
对象如何创建呢?直接使用 _instence=new T()
是会报错的。原因是T必须要有可以new的约束,解决方案:
- 给T添加new() 约束
- 使用反射(下面是反射实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| abstract class NormalSingleton<T> where T:NormalSingleton<T> { protected static T _instence = null; protected NormalSingleton(){}
public static T Singleton { get { if (_instence == null) { _instence = Activator.CreateInstance<T>(); } return _instence; } } }
|
使用方法:
1 2 3 4 5 6 7 8 9 10
| public class XXXManager : NormalSingleton<XXXManager> { private XXXManager(){}
public void AAA(){} }
XXXManager.Singleton.AAA();
|
那些不能通过new的类(如继承monobehavior的类)如何使用单例模板?
我们知道继承了Monobehavior的脚本只能挂载到GameObject上使用。上面的方式就不能使用了。
首先,要想实现Mono的单例,就要保证:
- 全局只有一个GaneObject挂载该Mono脚本,并且只能挂载一个。
- 可以接受Mono脚本的生命周期,以及单例的销毁
代码时实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour { protected static T _instence = null;
public static T Singleton { get { if (_instence == null) { string name = typeof(T).Name; var obj = GameObject.Find(name);
if (obj == null) obj = new GameObject(name);
_instence = obj.GetComponent<T>(); if (_instence == null) _instence = obj.AddComponent<T>(); DontDestroyOnLoad(obj); }
return _instence; } }
protected virtual void OnDestroy() { _instence = null; } }
|
使用只需要继承MonoSingleton
即可。