This commit is contained in:
陈思海 2026-04-15 14:51:57 +08:00
parent 6f897a7af7
commit 6ceaa2509b
4 changed files with 59 additions and 14 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using UnityEngine; using UnityEngine;
namespace AlicizaX.UI namespace AlicizaX.UI
@ -8,12 +9,30 @@ namespace AlicizaX.UI
{ {
private float[] itemLengths = Array.Empty<float>(); private float[] itemLengths = Array.Empty<float>();
private float[] itemPositions = Array.Empty<float>(); private float[] itemPositions = Array.Empty<float>();
private int rentedArraySize = 0;
private Vector2 firstItemSize = Vector2.zero; private Vector2 firstItemSize = Vector2.zero;
private int cachedItemCount = -1; private int cachedItemCount = -1;
private bool positionCacheDirty = true; private bool positionCacheDirty = true;
public MixedLayoutManager() { } public MixedLayoutManager() { }
~MixedLayoutManager()
{
ReturnRentedArrays();
}
private void ReturnRentedArrays()
{
if (rentedArraySize > 0)
{
ArrayPool<float>.Shared.Return(itemLengths);
ArrayPool<float>.Shared.Return(itemPositions);
itemLengths = Array.Empty<float>();
itemPositions = Array.Empty<float>();
rentedArraySize = 0;
}
}
public override Vector2 CalculateContentSize() public override Vector2 CalculateContentSize()
{ {
positionCacheDirty = true; positionCacheDirty = true;
@ -144,10 +163,18 @@ namespace AlicizaX.UI
private void RebuildPositionCache(int itemCount) private void RebuildPositionCache(int itemCount)
{ {
if (itemLengths.Length != itemCount) // ArrayPool.Rent 返回的数组长度 >= 请求长度rentedArraySize 记录实际容量。
// 仅当 itemCount 超出当前容量时才归还并重新租用,缩容时复用原数组。
if (itemCount > rentedArraySize)
{ {
itemLengths = itemCount > 0 ? new float[itemCount] : Array.Empty<float>(); ReturnRentedArrays();
itemPositions = itemCount > 0 ? new float[itemCount] : Array.Empty<float>();
if (itemCount > 0)
{
itemLengths = ArrayPool<float>.Shared.Rent(itemCount);
itemPositions = ArrayPool<float>.Shared.Rent(itemCount);
rentedArraySize = itemLengths.Length;
}
} }
firstItemSize = itemCount > 0 ? viewProvider.CalculateViewSize(0) : Vector2.zero; firstItemSize = itemCount > 0 ? viewProvider.CalculateViewSize(0) : Vector2.zero;

View File

@ -188,9 +188,12 @@ namespace AlicizaX.UI
private void TrackAllocate(string typeName) private void TrackAllocate(string typeName)
{ {
int active = GetActiveCount(typeName) + 1; activeCountByType.TryGetValue(typeName, out int active);
active++;
activeCountByType[typeName] = active; activeCountByType[typeName] = active;
if (active > GetPeakActiveCount(typeName))
peakActiveByType.TryGetValue(typeName, out int peak);
if (active > peak)
{ {
peakActiveByType[typeName] = active; peakActiveByType[typeName] = active;
} }
@ -198,14 +201,21 @@ namespace AlicizaX.UI
private void TrackFree(string typeName) private void TrackFree(string typeName)
{ {
int active = GetActiveCount(typeName); activeCountByType.TryGetValue(typeName, out int active);
if (active > 0) if (active > 0)
{ {
activeCountByType[typeName] = active - 1; activeCountByType[typeName] = active - 1;
} }
int recommendedMax = GetPeakActiveCount(typeName) + 1; peakActiveByType.TryGetValue(typeName, out int peak);
if (recommendedMax > GetMaxSize(typeName)) int recommendedMax = peak + 1;
typeSize.TryGetValue(typeName, out int currentMax);
if (currentMax <= 0)
{
currentMax = defaultMaxSizePerType;
}
if (recommendedMax > currentMax)
{ {
typeSize[typeName] = recommendedMax; typeSize[typeName] = recommendedMax;
} }

View File

@ -7,6 +7,7 @@ namespace AlicizaX.UI
{ {
private readonly MixedObjectPool<ViewHolder> objectPool; private readonly MixedObjectPool<ViewHolder> objectPool;
private readonly Dictionary<string, ViewHolder> templatesByName = new(StringComparer.Ordinal); private readonly Dictionary<string, ViewHolder> templatesByName = new(StringComparer.Ordinal);
private readonly Dictionary<string, int> warmCounts = new(StringComparer.Ordinal);
public override string PoolStats => public override string PoolStats =>
$"hits={objectPool.HitCount}, misses={objectPool.MissCount}, destroys={objectPool.DestroyCount}"; $"hits={objectPool.HitCount}, misses={objectPool.MissCount}, destroys={objectPool.DestroyCount}";
@ -85,8 +86,8 @@ namespace AlicizaX.UI
int itemCount = GetItemCount(); int itemCount = GetItemCount();
int start = Math.Max(0, LayoutManager.GetStartIndex()); int start = Math.Max(0, LayoutManager.GetStartIndex());
int end = Math.Min(itemCount - 1, start + warmCount - 1); int end = Math.Min(itemCount - 1, start + warmCount - 1);
Dictionary<string, int> counts = new(StringComparer.Ordinal);
warmCounts.Clear();
for (int index = start; index <= end; index++) for (int index = start; index <= end; index++)
{ {
string viewName = Adapter.GetViewName(index); string viewName = Adapter.GetViewName(index);
@ -95,11 +96,11 @@ namespace AlicizaX.UI
continue; continue;
} }
counts.TryGetValue(viewName, out int count); warmCounts.TryGetValue(viewName, out int count);
counts[viewName] = count + 1; warmCounts[viewName] = count + 1;
} }
foreach (var pair in counts) foreach (var pair in warmCounts)
{ {
int targetCount = pair.Value + Math.Max(1, LayoutManager.Unit); int targetCount = pair.Value + Math.Max(1, LayoutManager.Unit);
objectPool.EnsureCapacity(pair.Key, targetCount); objectPool.EnsureCapacity(pair.Key, targetCount);

View File

@ -177,7 +177,7 @@ namespace AlicizaX.UI
if (!viewHoldersByDataIndex.TryGetValue(viewHolder.DataIndex, out List<ViewHolder> holders)) if (!viewHoldersByDataIndex.TryGetValue(viewHolder.DataIndex, out List<ViewHolder> holders))
{ {
holders = new List<ViewHolder>(); holders = new List<ViewHolder>(1);
viewHoldersByDataIndex[viewHolder.DataIndex] = holders; viewHoldersByDataIndex[viewHolder.DataIndex] = holders;
} }
@ -199,7 +199,14 @@ namespace AlicizaX.UI
return; return;
} }
holders.Remove(viewHolder); // 用末尾元素覆盖目标项再移除末位,避免 List.Remove 的线性搜索+内存搬移。
int idx = holders.LastIndexOf(viewHolder);
if (idx >= 0)
{
holders[idx] = holders[holders.Count - 1];
holders.RemoveAt(holders.Count - 1);
}
if (holders.Count == 0) if (holders.Count == 0)
{ {
viewHoldersByDataIndex.Remove(viewHolder.DataIndex); viewHoldersByDataIndex.Remove(viewHolder.DataIndex);