优化对象池释放逻辑,计数器仅作为对象池复用封装
This commit is contained in:
@@ -105,6 +105,7 @@ namespace XericUI.XTable.Rendering.Elements
|
||||
if (parent != null && item.Root.transform.parent != parent)
|
||||
item.Root.transform.SetParent(parent, false);
|
||||
item.CellId = cellId;
|
||||
item.Root.SetActive(true);
|
||||
item.Root.name = name;
|
||||
ResetImageDefaults(item);
|
||||
BindStyle(item.Style, styleNamespace);
|
||||
@@ -121,6 +122,7 @@ namespace XericUI.XTable.Rendering.Elements
|
||||
if (parent != null && item.Root.transform.parent != parent)
|
||||
item.Root.transform.SetParent(parent, false);
|
||||
item.CellId = cellId;
|
||||
item.Root.SetActive(true);
|
||||
item.Root.name = name;
|
||||
ResetTextDefaults(item);
|
||||
BindStyle(item.Style, styleNamespace);
|
||||
@@ -148,30 +150,41 @@ namespace XericUI.XTable.Rendering.Elements
|
||||
OnStyleChangeDetected?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>归还所有槽位至 FrozenSlots(全量刷新时调用)</summary>
|
||||
/// <summary>归还所有槽位(全量刷新时调用)。被归还对象会隐藏,直到下次 Acquire。</summary>
|
||||
public void ReturnAll()
|
||||
{
|
||||
// Unbind 所有活跃槽位的样式回调
|
||||
m_ImagePool.ReturnAll();
|
||||
m_TextPool.ReturnAll();
|
||||
m_ImagePool.ReturnAll(HideImage);
|
||||
m_TextPool.ReturnAll(HideText);
|
||||
}
|
||||
|
||||
/// <summary>归还单个 Image 池对象(通过 CellId 定位槽位并移入 FrozenSlots)</summary>
|
||||
/// <summary>归还单个 Image 池对象并隐藏。</summary>
|
||||
public void ReleaseImage(PooledImage item)
|
||||
{
|
||||
if (item == null) return;
|
||||
item.Style?.Unbind();
|
||||
HideImage(item);
|
||||
m_ImagePool.ReleaseByObject(item);
|
||||
}
|
||||
|
||||
/// <summary>归还单个 Text 池对象(通过 CellId 定位槽位并移入 FrozenSlots)</summary>
|
||||
/// <summary>归还单个 Text 池对象并隐藏。</summary>
|
||||
public void ReleaseText(PooledText item)
|
||||
{
|
||||
if (item == null) return;
|
||||
item.Style?.Unbind();
|
||||
HideText(item);
|
||||
m_TextPool.ReleaseByObject(item);
|
||||
}
|
||||
|
||||
private static void HideImage(PooledImage item)
|
||||
{
|
||||
item.Style?.Unbind();
|
||||
item.Root.SetActive(false);
|
||||
}
|
||||
|
||||
private static void HideText(PooledText item)
|
||||
{
|
||||
item.Style?.Unbind();
|
||||
item.Root.SetActive(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 默认值重置
|
||||
|
||||
@@ -1,195 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.XTable.Rendering.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// 预制体对象池——按原型分组管理,每个原型使用计数器槽位绑定 cellId 到预制体实例。
|
||||
/// 预制体不参与 FrozenSlots 流转:始终绑定在 cellId 槽位上,仅在表格销毁时释放。
|
||||
/// </summary>
|
||||
public class PrefabPrototypePool
|
||||
{
|
||||
#region 内部类型
|
||||
/// <summary>按原型管理的预制体池。占用槽位绑定 cellId,归还后隐藏并留在同原型待命池中复用。</summary>
|
||||
public class PrefabPrototypePool
|
||||
{
|
||||
private class PrefabPool
|
||||
{
|
||||
public readonly Dictionary<int, GameObject> ActiveSlots = new();
|
||||
public readonly Stack<GameObject> Standby = new();
|
||||
}
|
||||
|
||||
private class PrefabPool
|
||||
{
|
||||
public GameObject Prototype;
|
||||
/// <summary>cellId → 预制体实例 的计数器槽位绑定</summary>
|
||||
public Dictionary<int, GameObject> Slots = new Dictionary<int, GameObject>();
|
||||
}
|
||||
private readonly Dictionary<GameObject, PrefabPool> m_Pools = new();
|
||||
private readonly Transform m_Parent;
|
||||
private const int MAX_PREFAB_ITEMS = 256;
|
||||
private int m_TotalCreated;
|
||||
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
|
||||
private static readonly Vector2 AnchorTopLeft = new(0, 1);
|
||||
private static readonly Vector2 PivotTopLeft = new(0, 1);
|
||||
|
||||
#endregion
|
||||
public PrefabPrototypePool(Transform parent) => m_Parent = parent;
|
||||
|
||||
#region 字段
|
||||
public GameObject Acquire(GameObject prefab, int cellId, Transform parent = null)
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
PrefabPool pool = GetOrCreatePool(prefab);
|
||||
Transform targetParent = parent ?? m_Parent;
|
||||
if (pool.ActiveSlots.TryGetValue(cellId, out GameObject existing))
|
||||
{
|
||||
Prepare(existing, targetParent, cellId);
|
||||
return existing;
|
||||
}
|
||||
|
||||
private Dictionary<GameObject, PrefabPool> m_Pools = new Dictionary<GameObject, PrefabPool>();
|
||||
private Transform m_Parent;
|
||||
GameObject instance = pool.Standby.Count > 0 ? pool.Standby.Pop() : Create(prefab, targetParent);
|
||||
if (instance == null) return null;
|
||||
pool.ActiveSlots[cellId] = instance;
|
||||
Prepare(instance, targetParent, cellId);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>渲染项目总数上限(全局,跨所有原型)</summary>
|
||||
private const int MAX_PREFAB_ITEMS = 256;
|
||||
private int m_TotalCreated;
|
||||
public void ReturnAll(Action<GameObject> beforeRelease = null)
|
||||
{
|
||||
foreach (PrefabPool pool in m_Pools.Values)
|
||||
{
|
||||
foreach (GameObject instance in pool.ActiveSlots.Values)
|
||||
{
|
||||
beforeRelease?.Invoke(instance);
|
||||
instance.SetActive(false);
|
||||
pool.Standby.Push(instance);
|
||||
}
|
||||
pool.ActiveSlots.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
|
||||
public bool Release(GameObject instance, Action<GameObject> beforeRelease = null)
|
||||
{
|
||||
if (instance == null) return false;
|
||||
foreach (PrefabPool pool in m_Pools.Values)
|
||||
{
|
||||
int foundCellId = -1;
|
||||
foreach (var pair in pool.ActiveSlots)
|
||||
{
|
||||
if (pair.Value != instance) continue;
|
||||
foundCellId = pair.Key;
|
||||
break;
|
||||
}
|
||||
if (foundCellId < 0) continue;
|
||||
beforeRelease?.Invoke(instance);
|
||||
pool.ActiveSlots.Remove(foundCellId);
|
||||
instance.SetActive(false);
|
||||
pool.Standby.Push(instance);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly Vector2 AnchorTopLeft = new Vector2(0, 1);
|
||||
private static readonly Vector2 PivotTopLeft = new Vector2(0, 1);
|
||||
public void DestroyAll()
|
||||
{
|
||||
foreach (PrefabPool pool in m_Pools.Values)
|
||||
{
|
||||
foreach (GameObject instance in pool.ActiveSlots.Values) KillObject(instance);
|
||||
foreach (GameObject instance in pool.Standby) KillObject(instance);
|
||||
}
|
||||
m_Pools.Clear();
|
||||
m_TotalCreated = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
private PrefabPool GetOrCreatePool(GameObject prefab)
|
||||
{
|
||||
if (!m_Pools.TryGetValue(prefab, out PrefabPool pool))
|
||||
{
|
||||
pool = new PrefabPool();
|
||||
m_Pools.Add(prefab, pool);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
#region 构造 / 销毁
|
||||
private GameObject Create(GameObject prefab, Transform parent)
|
||||
{
|
||||
if (m_TotalCreated >= MAX_PREFAB_ITEMS)
|
||||
{
|
||||
Debug.LogWarning($"[XericUIActionTable] Prefab 池已达上限 {MAX_PREFAB_ITEMS},无法分配新实例");
|
||||
return null;
|
||||
}
|
||||
GameObject instance = UnityEngine.Object.Instantiate(prefab, parent);
|
||||
instance.hideFlags = POOL_FLAGS;
|
||||
m_TotalCreated++;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public PrefabPrototypePool(Transform parent)
|
||||
{
|
||||
m_Parent = parent;
|
||||
}
|
||||
private static void Prepare(GameObject instance, Transform parent, int cellId)
|
||||
{
|
||||
if (parent != null && instance.transform.parent != parent)
|
||||
instance.transform.SetParent(parent, false);
|
||||
instance.name = $"_prefab_{cellId}";
|
||||
RectTransform rt = instance.GetComponent<RectTransform>() ?? instance.AddComponent<RectTransform>();
|
||||
rt.anchorMin = AnchorTopLeft;
|
||||
rt.anchorMax = AnchorTopLeft;
|
||||
rt.pivot = PivotTopLeft;
|
||||
instance.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>彻底销毁所有池对象(所有原型的活跃槽位)</summary>
|
||||
public void DestroyAll()
|
||||
{
|
||||
foreach (var kv in m_Pools)
|
||||
{
|
||||
var pool = kv.Value;
|
||||
foreach (var instance in pool.Slots.Values)
|
||||
KillObject(instance);
|
||||
pool.Slots.Clear();
|
||||
}
|
||||
m_Pools.Clear();
|
||||
m_TotalCreated = 0;
|
||||
}
|
||||
|
||||
private static void KillObject(GameObject go)
|
||||
{
|
||||
if (go == null) return;
|
||||
if (Application.isPlaying)
|
||||
Object.Destroy(go);
|
||||
else
|
||||
Object.DestroyImmediate(go);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 池操作
|
||||
|
||||
/// <summary>
|
||||
/// 为指定 cellId 获取预制体实例。
|
||||
/// 若该 cellId 已有绑定的实例,直接返回;否则创建新实例(需未达上限)。
|
||||
/// </summary>
|
||||
public GameObject Acquire(GameObject prefab, int cellId, Transform parent = null)
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
|
||||
var pool = GetOrCreatePool(prefab);
|
||||
Transform targetParent = parent ?? m_Parent;
|
||||
|
||||
// 已绑定 → 直接复用并迁移到当前渲染区域。
|
||||
if (pool.Slots.TryGetValue(cellId, out var existing))
|
||||
{
|
||||
if (targetParent != null && existing.transform.parent != targetParent)
|
||||
existing.transform.SetParent(targetParent, false);
|
||||
return existing;
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
if (m_TotalCreated >= MAX_PREFAB_ITEMS)
|
||||
{
|
||||
Debug.LogWarning($"[XericUIActionTable] Prefab 池已达上限 {MAX_PREFAB_ITEMS},无法分配新实例");
|
||||
return null;
|
||||
}
|
||||
|
||||
var instance = Object.Instantiate(prefab, targetParent);
|
||||
instance.hideFlags = POOL_FLAGS;
|
||||
instance.name = $"_prefab_{cellId}";
|
||||
m_TotalCreated++;
|
||||
|
||||
// 强制 RectTransform 左上角坐标系
|
||||
RectTransform rt = instance.GetComponent<RectTransform>();
|
||||
if (rt == null) rt = instance.AddComponent<RectTransform>();
|
||||
rt.anchorMin = AnchorTopLeft;
|
||||
rt.anchorMax = AnchorTopLeft;
|
||||
rt.pivot = PivotTopLeft;
|
||||
|
||||
pool.Slots[cellId] = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 归还指定实例至槽位(仅从槽位移除 + 销毁对象,不在滚动时调用)。
|
||||
/// 预制体的回收仅发生在表格销毁时。
|
||||
/// </summary>
|
||||
public void Release(GameObject instance)
|
||||
{
|
||||
if (instance == null) return;
|
||||
|
||||
foreach (var kv in m_Pools)
|
||||
{
|
||||
var pool = kv.Value;
|
||||
// 查找并移除
|
||||
int? foundKey = null;
|
||||
foreach (var slot in pool.Slots)
|
||||
{
|
||||
if (slot.Value == instance)
|
||||
{
|
||||
foundKey = slot.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundKey.HasValue)
|
||||
{
|
||||
pool.Slots.Remove(foundKey.Value);
|
||||
m_TotalCreated--;
|
||||
KillObject(instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 不属于任何池(异常情况),直接销毁
|
||||
KillObject(instance);
|
||||
}
|
||||
|
||||
/// <summary>销毁所有按 cellId 绑定的实例,并在销毁前通知调用方。</summary>
|
||||
public void ReleaseAll(System.Action<GameObject> beforeRelease = null)
|
||||
{
|
||||
foreach (var pool in m_Pools.Values)
|
||||
{
|
||||
foreach (var instance in pool.Slots.Values)
|
||||
{
|
||||
beforeRelease?.Invoke(instance);
|
||||
KillObject(instance);
|
||||
}
|
||||
pool.Slots.Clear();
|
||||
}
|
||||
m_Pools.Clear();
|
||||
m_TotalCreated = 0;
|
||||
}
|
||||
|
||||
/// <summary>结束当前渲染周期。实例保留在全局槽位中,以便跨区域复用。</summary>
|
||||
public void ReturnAll()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>获取当前所有活跃实例(用于外部遍历回调)</summary>
|
||||
public IReadOnlyCollection<GameObject> GetActive(GameObject prefab)
|
||||
{
|
||||
if (prefab == null || !m_Pools.TryGetValue(prefab, out var pool))
|
||||
return System.Array.Empty<GameObject>();
|
||||
return pool.Slots.Values;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部方法
|
||||
|
||||
private PrefabPool GetOrCreatePool(GameObject prefab)
|
||||
{
|
||||
if (!m_Pools.TryGetValue(prefab, out var pool))
|
||||
{
|
||||
pool = new PrefabPool { Prototype = prefab };
|
||||
m_Pools[prefab] = pool;
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
private static void KillObject(GameObject go)
|
||||
{
|
||||
if (go == null) return;
|
||||
if (Application.isPlaying) UnityEngine.Object.Destroy(go);
|
||||
else UnityEngine.Object.DestroyImmediate(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,15 +147,20 @@ namespace XericUI.XTable.Rendering.Elements
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>释放所有活跃槽位,但保留其所属计数器。</summary>
|
||||
public void ReturnAll()
|
||||
/// <summary>释放所有活跃槽位;回调在槽位变为空闲前执行。</summary>
|
||||
public void ReturnAll(Action<T> beforeRelease = null)
|
||||
{
|
||||
for (int ci = 0; ci < POOL_SIZE; ci++)
|
||||
{
|
||||
var counter = m_Counters[ci];
|
||||
if (counter == null) continue;
|
||||
for (int i = 0; i < counter.Slots.Count; i++)
|
||||
counter.Slots[i].CellId = -1;
|
||||
{
|
||||
var slot = counter.Slots[i];
|
||||
if (slot.CellId == -1) continue;
|
||||
beforeRelease?.Invoke(slot.PooledObject);
|
||||
slot.CellId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user