明天十一放假,今天不知什么原因看到一篇unity單例模式的介紹,瞬間來了戾氣。
(一)最簡單的單利
public class WebRequestUtility : MonoBehaviour { public static WebRequestUtility Instance; private void Awake() { Instance = this; } }
這是最簡單有效,最實用最沒問題的單利模式。如果說存在什么問題,那就是在別的腳本awake中引用,若果其他腳本中的awake比上述awke先執行,則會報空引用。此問題通過設置腳本執行順序課解決。題外話,一般初始化啟動盡量用start,除非是非常確定要先運行用awake,不要為了先執行而用awake;
(二)構造函數法
public class WebRequestUtility : MonoBehaviour { public static WebRequestUtility Instance; WebRequestUtility() { Instance = this; } }
構造函數中初始化instance最先執行,會在awake之前(官方為找到直接說明,但是鑒於腳本先初始化而后運行,會比awake先執行,事實也是這樣,如果有人發現有問題再議,來互懟)。但在2017unity版本中遇到過bug(緊遇到一次),即剛開始運行時,構造函數多次運行,原因未找到。
(三)DontDestroyOnLoad情況下的單例模式
有時候單例不能隨着場景的加載而消失,需要一直存在,所以需要不能銷毀,但是加載時場景中就會存在兩個單例(這個說法本身有問題,即本來已經有單例,但是在場景加載時awake又重新運行的情況)
public class WebRequestUtility : MonoBehaviour { public bool bDontDestroyOnLoad = false; public static WebRequestUtility Instance; private void InitializeInstance() { if (Instance != null & Instance == this) return; if(bDontDestroyOnLoad) { if(Instance==null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } else { Instance = this; } } private void Awake() { InitializeInstance(); } }
上述代碼參考自unity的NetworkManager組件的源代碼。邏輯很簡單,只允許有一個單例,引用時,除了保證單例已經初始化(不為空),還要保證單例==this。否則就是單例被重新賦值了,即在別的地方又重新初始化,既然使用單例模式這是不允許的。
(四)靜態屬性或者靜態方法法
public class WebRequestUtility : MonoBehaviour { private static WebRequestUtility instance; public static WebRequestUtility Instance { get { if(instance==null) { instance = new WebRequestUtility(); } return instance; } } }
這種也是一種比較好的方法,但是沒有(一)(二)簡潔,一般用於獲取單例時需要再初始化等問題時,對於大部分單例模式(一)(二)已經夠用。
(四)單例模式亂象(真正互懟開始)
本想再這部分直接開始懟的,但是寫到這人戾氣已經沒了,還是好好學習的好啊,學習讓我心平氣和。繼續說某度出的unity單例的問題。
1)單例變”多例“
從他代碼角度確實時要單例,但是經過幾行代碼后變成多例,然后又經過幾行代碼,然后可從多個事例中return一個”單例“(???),本想直接上鏈接懟的,想想還是算了。可以看看此文https://blog.csdn.net/qq_15267341/article/details/54232854,多簡介,多明了。
2)使用鎖(lock)
使用鎖沒有任何問題,但是unity中不推薦使用。再.net中單例模式必須加鎖,因為再多線程中統一使用單例會出現沖突等等問題,比如死鎖,或者邏輯未理清出問題。但是unity是單線程操作,通過協程來實現“異步“操作,所以不存在此問題。當然也存在開線程的問題,但是開線程或者異步操作只針對純數據層面的操作,因為在非主線程中是無法進行組件操作的(通俗講就是操作unity自定義的東西)。所以在unity中異步或者開線程時,基本可以避免在其他線程中調用主線程中的單例情況(針對游戲層面,vr ar來說)。在極少數非要進行相關處理的(一般是在回調時出現),也可以通過在update中實時檢測來解決。當然了,如果你的單例不涉及到unity相關組件操作,那也就不用繼承mono,就可以用純C#的語法來處理了。
最后竟然一點戾氣都沒有了。。。。。。。。。。。。。。。。。。