unity單例封裝

# 1.前言
本篇主要針對Unity單例模式,完成一個能夠重複繼承使用的抽象類,減小重複的工做與代碼。同時,對存在的多種單例進行優劣分析。
# 2.Unity單例問題分析
## 2.1 單例原則
單例要知足如下兩個原則:
### 2.1.1 單一原則
即不能存在兩個單例對象,這看起來是一句廢話,且在C#編程中不會出現,但在Unity中進行組件化編程的時候卻會存在。由於unity繼承自Monobehavior的類是一個組件能夠經過掛載造成一個實例。不用手動new產生。這就容易違背此原則。
### 2.1.2 功能封裝原則
單例是爲了將特定功能且功能關聯的方法,封裝在一個模塊能,方便處理各類問題。而且避免各類重複產生實例對象,因此單例通常都會隱藏public構造函數。
## 2.2 存在問題
### 2.2.1 單例原則違背
目前,網絡充斥這大量單例模式,都或多或少存在各類問題,各類解決方案臃腫,沒有必要進行如此處理。[點擊查看詳細敘述](https://blog.csdn.net/qq_37833413/article/details/102613583)。
本文最後也會對不推薦的單例寫法進行分析。
### 2.2.2 單例濫用
採用unity進行開發時,不可避免的會涉及到ui的切換以及相互調用。在不少教程或者網絡教學視頻中,大量採用單例,筆者曾看過一個教程,大部分腳本都採用單例進行處理。好處是增長靈活性,方便調用與切換,壞處則時不易維護,增長開銷,尤爲是當用到下文不推薦的方式。因此**必定不要爲了方便不一樣腳本之間相互調用而採用單例**。
# 3.簡單單例模式
## 3.1 推薦方式
此方式最簡單粗暴,有效。惟一缺陷是看着一點都不高端。編程

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;安全

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T instance;網絡

public static T Instance
{
get
{
return instance;
}
}函數

protected virtual void Awake()
{
instance = (T)this;
}
}
```
## 3.2 使用FindObjectOfType的單例
### 3.2.1 單例代碼
代碼以下所示,此方法看起來很完美,考慮周全,其實存在不少問題與隱患。組件化

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;性能

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T instance;ui

public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
}this

if (instance != null || !instance.initialized)
{
instance.StartComponent();
}.net

return instance;
}
}線程

protected bool initialized = false;

protected virtual void StartComponent()
{
initialized = true;
}

private void Start()
{
if (!initialized)
{
StartComponent();
}
}
}
```
### 3.2.2 FindObjectOfType的使用
FindObjectOfType此方法是遍歷全部激活遊戲物體,查找組件,返回第一個找到的對象。此方法有如下幾個問題:

**問題一**:**性能開銷,要查找全部激活物體以及腳本,性能消耗不可避免。若是全部的單例都採用此方式,則在剛啓動時一塊兒性能開銷峯值。**

**問題二:此方法須要遊戲物體激活狀態使用,即若是遊戲物體未激活狀態調用此遊戲物體,則單例爲空,若是不知道FindObjectOfType的用法,可能不會找問題,因此上述單例腳本中StartComponent也就沒有太大意義。**

## 3.3 在單例中生成遊戲物體
### 3.3.1 腳本以下:
```csharp
using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T instance;

public static T Instance
{
get
{
if (instance == null)
{
var instances =Resources.FindObjectsOfTypeAll(typeof(T)) as T[];

if (instances.Length > 0)
{
instance = instances[0];
}

if (instance == null)
{
GameObject InstanceObj = new GameObject("MonoSingleton");
instance = InstanceObj.AddComponent<T>();
}
}
return instance;
}
}
}
```
### 3.3.2 FindObjectsOfTypeAll使用
此部分問題同上,並且還不如使用FindObjectsOfType。
### 3.3.3 在自身建立遊戲物體
不建議在自身建立自身腳本所在的遊戲物體。當涉及到場景加載時銷燬遊戲物體時會有很大的風險,尤爲是當在OnDisable中調用此單例時。由於unity銷燬遊戲物體時是隨機的,當銷燬時會調用OnDisable方法。若是此單例已經銷燬,在別的腳本OnDestroy直接調用時,會引起生成新遊戲物體的錯誤。

# 4.C#的幾種單例
如下兩種方法不涉及Unity組件問題單例:
## 4.1 Unity中使用
當unity主線程中使用,不涉及線程安全問題時可用以下代碼:

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Singleton<T> where T : Singleton<T>,new()
{
private static T instance;

public static T Instance
{
get
{
if (instance == null)
{
instance = new T();
instance.Init();
}

return instance;
}
}

protected virtual void Init() { }
}
```
## 4.2 線程安全單例

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Singleton<T> where T : Singleton<T>, new()
{
private static object threadSafeLock = new object();

private static T instance;

public static T Instance
{
get
{
if (instance == null)
{
lock(threadSafeLock)
{
if (instance == null)
{
instance = new T();
instance.Init();
}
}
}

return instance;
}
}

protected virtual void Init() { }}```# 5.總結**總之在使用單例時,不要爲了靈活方便而頻繁使用單例。同時作好處理和規劃,防止單例未初始化而調用,同時慎用Awake方法,不要爲了能比start早執行而使用。**

相關文章
相關標籤/搜索