拓展編輯器 9 - 查找靜態引用的資源(預製)

遊戲運行過程中,由於邏輯過於複雜,可能會有資源被靜態變量引用,無法釋放,我們需要找到靜態引用資源的變量,來定位問題。

基本原理時,獲取我們的腳本的 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);
                }
            }
        }
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章