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的效果...