From 6ceaa2509b048694b5847fa2a922f664f039326a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Wed, 15 Apr 2026 14:51:57 +0800 Subject: [PATCH] 11 --- .../RecyclerView/Layout/MixedLayoutManager.cs | 33 +++++++++++++++++-- .../ObjectPool/MixedObjectPool.cs | 20 ++++++++--- .../ViewProvider/MixedViewProvider.cs | 9 ++--- .../RecyclerView/ViewProvider/ViewProvider.cs | 11 +++++-- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/Runtime/RecyclerView/Layout/MixedLayoutManager.cs b/Runtime/RecyclerView/Layout/MixedLayoutManager.cs index 1a56d75..aeabdf8 100644 --- a/Runtime/RecyclerView/Layout/MixedLayoutManager.cs +++ b/Runtime/RecyclerView/Layout/MixedLayoutManager.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using UnityEngine; namespace AlicizaX.UI @@ -8,12 +9,30 @@ namespace AlicizaX.UI { private float[] itemLengths = Array.Empty(); private float[] itemPositions = Array.Empty(); + private int rentedArraySize = 0; private Vector2 firstItemSize = Vector2.zero; private int cachedItemCount = -1; private bool positionCacheDirty = true; public MixedLayoutManager() { } + ~MixedLayoutManager() + { + ReturnRentedArrays(); + } + + private void ReturnRentedArrays() + { + if (rentedArraySize > 0) + { + ArrayPool.Shared.Return(itemLengths); + ArrayPool.Shared.Return(itemPositions); + itemLengths = Array.Empty(); + itemPositions = Array.Empty(); + rentedArraySize = 0; + } + } + public override Vector2 CalculateContentSize() { positionCacheDirty = true; @@ -144,10 +163,18 @@ namespace AlicizaX.UI private void RebuildPositionCache(int itemCount) { - if (itemLengths.Length != itemCount) + // ArrayPool.Rent 返回的数组长度 >= 请求长度,rentedArraySize 记录实际容量。 + // 仅当 itemCount 超出当前容量时才归还并重新租用,缩容时复用原数组。 + if (itemCount > rentedArraySize) { - itemLengths = itemCount > 0 ? new float[itemCount] : Array.Empty(); - itemPositions = itemCount > 0 ? new float[itemCount] : Array.Empty(); + ReturnRentedArrays(); + + if (itemCount > 0) + { + itemLengths = ArrayPool.Shared.Rent(itemCount); + itemPositions = ArrayPool.Shared.Rent(itemCount); + rentedArraySize = itemLengths.Length; + } } firstItemSize = itemCount > 0 ? viewProvider.CalculateViewSize(0) : Vector2.zero; diff --git a/Runtime/RecyclerView/ObjectPool/MixedObjectPool.cs b/Runtime/RecyclerView/ObjectPool/MixedObjectPool.cs index 5c20619..ba10e02 100644 --- a/Runtime/RecyclerView/ObjectPool/MixedObjectPool.cs +++ b/Runtime/RecyclerView/ObjectPool/MixedObjectPool.cs @@ -188,9 +188,12 @@ namespace AlicizaX.UI private void TrackAllocate(string typeName) { - int active = GetActiveCount(typeName) + 1; + activeCountByType.TryGetValue(typeName, out int active); + active++; activeCountByType[typeName] = active; - if (active > GetPeakActiveCount(typeName)) + + peakActiveByType.TryGetValue(typeName, out int peak); + if (active > peak) { peakActiveByType[typeName] = active; } @@ -198,14 +201,21 @@ namespace AlicizaX.UI private void TrackFree(string typeName) { - int active = GetActiveCount(typeName); + activeCountByType.TryGetValue(typeName, out int active); if (active > 0) { activeCountByType[typeName] = active - 1; } - int recommendedMax = GetPeakActiveCount(typeName) + 1; - if (recommendedMax > GetMaxSize(typeName)) + peakActiveByType.TryGetValue(typeName, out int peak); + int recommendedMax = peak + 1; + typeSize.TryGetValue(typeName, out int currentMax); + if (currentMax <= 0) + { + currentMax = defaultMaxSizePerType; + } + + if (recommendedMax > currentMax) { typeSize[typeName] = recommendedMax; } diff --git a/Runtime/RecyclerView/ViewProvider/MixedViewProvider.cs b/Runtime/RecyclerView/ViewProvider/MixedViewProvider.cs index d837a88..d61c382 100644 --- a/Runtime/RecyclerView/ViewProvider/MixedViewProvider.cs +++ b/Runtime/RecyclerView/ViewProvider/MixedViewProvider.cs @@ -7,6 +7,7 @@ namespace AlicizaX.UI { private readonly MixedObjectPool objectPool; private readonly Dictionary templatesByName = new(StringComparer.Ordinal); + private readonly Dictionary warmCounts = new(StringComparer.Ordinal); public override string PoolStats => $"hits={objectPool.HitCount}, misses={objectPool.MissCount}, destroys={objectPool.DestroyCount}"; @@ -85,8 +86,8 @@ namespace AlicizaX.UI int itemCount = GetItemCount(); int start = Math.Max(0, LayoutManager.GetStartIndex()); int end = Math.Min(itemCount - 1, start + warmCount - 1); - Dictionary counts = new(StringComparer.Ordinal); + warmCounts.Clear(); for (int index = start; index <= end; index++) { string viewName = Adapter.GetViewName(index); @@ -95,11 +96,11 @@ namespace AlicizaX.UI continue; } - counts.TryGetValue(viewName, out int count); - counts[viewName] = count + 1; + warmCounts.TryGetValue(viewName, out int count); + warmCounts[viewName] = count + 1; } - foreach (var pair in counts) + foreach (var pair in warmCounts) { int targetCount = pair.Value + Math.Max(1, LayoutManager.Unit); objectPool.EnsureCapacity(pair.Key, targetCount); diff --git a/Runtime/RecyclerView/ViewProvider/ViewProvider.cs b/Runtime/RecyclerView/ViewProvider/ViewProvider.cs index 20eb0a4..b63f088 100644 --- a/Runtime/RecyclerView/ViewProvider/ViewProvider.cs +++ b/Runtime/RecyclerView/ViewProvider/ViewProvider.cs @@ -177,7 +177,7 @@ namespace AlicizaX.UI if (!viewHoldersByDataIndex.TryGetValue(viewHolder.DataIndex, out List holders)) { - holders = new List(); + holders = new List(1); viewHoldersByDataIndex[viewHolder.DataIndex] = holders; } @@ -199,7 +199,14 @@ namespace AlicizaX.UI 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) { viewHoldersByDataIndex.Remove(viewHolder.DataIndex);