C#中一個物件的函式, 如果被參考了, 也會導致物件無法被回收, 雖然實際使用中幾率很小, 還是記錄一下.
using UnityEngine; public class MemoryLeakTest : MonoBehaviour { public class Event { public void Call() { Debug.Log("Event Call"); } ~Event() { Debug.Log("Event Die"); } } System.Action call = null; Event _event = null; private void OnGUI() { if(GUI.Button(new Rect(100, 100, 100, 50), "Event Call")) { _event = new Event(); _event.Call(); call += _event.Call; _event = null; } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手動GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
創建一個物件, 把物件的Call方法加到Action上, 然后置空參考, 只要物件的方法被參考了, 這樣就成了無法GC的物件了.
這個問題的發生屬于個人問題, 那么怎樣從結構上來避免呢, 如果使用一個弱參考能否避免呢 :
using UnityEngine; public class MonoEvent : MonoBehaviour { public string mark; public void Call() { Debug.Log("MonoEvent Call " + mark); } ~MonoEvent() { Debug.Log("MonoEvent Die " + mark); } }
參考物件MonoEvent
using UnityEngine; public class WeakEventTest : MonoBehaviour { System.WeakReference weakCall = null; private void OnGUI() { if(GUI.Button(new Rect(100, 150, 100, 50), "MonoEvent Call")) { var monoEvent = new GameObject().AddComponent<MonoEvent>(); monoEvent.mark = "111"; monoEvent.Call(); weakCall = new System.WeakReference(new System.Action(monoEvent.Call)); UnityEngine.Object.Destroy(monoEvent.gameObject); } if(GUI.Button(new Rect(100, 210, 100, 50), "手動Call")) { var call = weakCall.Target as System.Action; if(call != null) { call.Invoke(); } else { Debug.Log("參考沒了"); } } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手動GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
運行點擊 "MonoEvent Call" 后直接就能析構了, 說明弱參考沒有影響到GC. 可是反過來看GC是不是會影響弱參考 :
using UnityEngine; public class WeakEventTest : MonoBehaviour { System.WeakReference weakCall = null; MonoEvent _monoEvent; private void OnGUI() { if(GUI.Button(new Rect(100, 150, 100, 50), "MonoEvent Call")) { var monoEvent = new GameObject().AddComponent<MonoEvent>(); monoEvent.mark = "111"; monoEvent.Call(); weakCall = new System.WeakReference(new System.Action(monoEvent.Call)); _monoEvent = monoEvent; // 添加參考, 不被GC } if(GUI.Button(new Rect(100, 210, 100, 50), "手動Call")) { var call = weakCall.Target as System.Action; if(call != null) { call.Invoke(); } else { Debug.Log("參考沒了"); } } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手動GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
先創建物件, 然后手動GC, 在看看手動Call能否觸發, 結果悲劇了弱參考物件沒了:

這里參考物件參考的是新創建的System.Action, 它是新物件并且只被弱參考參考, 自然會被GC回收掉了, 所以在系統設計上沒有辦法用弱參考事件來對物件解耦, 達到不影響GC的效果...
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/21051.html
標籤:其他
