遊戲運行過程中,由於邏輯過於複雜,可能會有資源被靜態變量引用,無法釋放,我們需要找到靜態引用資源的變量,來定位問題。
基本原理時,獲取我們的腳本的 dll (Assembly-CSharp) 的所有類型,找到所有的靜態變量成員,如果成員是UnityEngine.Object 類型,收集依賴數據,最後將該靜態變量以用的資源打印出來
靜態引用腳本代碼,將該腳本添加到GameObject ,可以測試檢查邏輯
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StaticRef : MonoBehaviour
{
public static GameObject _prefab;
public GameObject prefab;
// Start is called before the first frame update
void Start()
{
_prefab = prefab;
}
// Update is called once per frame
void Update()
{
}
}
檢查代碼:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
public class CheckStaticReference
{
[MenuItem("X/Check Static Ref")]
static void Check()
{
Check("Assembly-CSharp-firstpass");
Check("Assembly-CSharp");
}
static void Check(string assName)
{
Assembly ass = null;
try
{
ass = Assembly.Load(assName);
if (ass == null)
return;
}
catch(Exception e)
{
Debug.LogException(e);
return;
}
foreach(Type t in ass.GetTypes())
{
HashSet<string> assets = new HashSet<string>();
FieldInfo[] arrFI = t.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach(FieldInfo fi in arrFI)
{
if(fi.FieldType.IsValueType == false)
{
// 我們查找的是靜態成員,因此調用GetValue(null)
Check(fi.GetValue(null), assets);
}
}
if(assets.Count > 0)
{
Debug.Log($"Find Static Reference. {t.ToString()}.cs. Count:{assets.Count}");
foreach(string a in assets)
{
Debug.Log(a);
}
}
}
}
static void Check(object obj, HashSet<string> assets)
{
if (obj == null)
return;
if(obj is UnityEngine.Object)
{
UnityEngine.Object[] depen = EditorUtility.CollectDependencies(new UnityEngine.Object[] { obj as UnityEngine.Object });
foreach(var item in depen)
{
string path = AssetDatabase.GetAssetPath(item);
if(string.IsNullOrEmpty(path) == false)
{
assets.Add(path);
}
}
}
else if(obj is IEnumerable)
{
foreach(object child in (obj as IEnumerable))
{
Check(child, assets);
}
}
else if(obj is System.Object)
{
if(obj.GetType().IsValueType == false)
{
FieldInfo[] arrFI = obj.GetType().GetFields();
foreach(FieldInfo fi in arrFI)
{
object o = fi.GetValue(obj);
if (o != obj)
Check(o, assets);
}
}
}
}
}