diff --git a/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs b/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs index b902d63..cf76e69 100644 --- a/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs +++ b/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs @@ -7,7 +7,7 @@ public enum UiTransitionPolicy { /// - /// 独占显示(下层页面 Pause + Hide) + /// 独占显示(下层页面 Pause + Suspend) /// Exclusive, diff --git a/GFramework.Game.Abstractions/ui/IUiFactory.cs b/GFramework.Game.Abstractions/ui/IUiFactory.cs index ad73996..665451f 100644 --- a/GFramework.Game.Abstractions/ui/IUiFactory.cs +++ b/GFramework.Game.Abstractions/ui/IUiFactory.cs @@ -11,75 +11,6 @@ public interface IUiFactory : IContextUtility /// 创建或获取UI页面实例 /// /// UI标识键 - /// 实例管理策略 /// UI页面实例 - IUiPageBehavior GetOrCreate(string uiKey, UiInstancePolicy policy = UiInstancePolicy.AlwaysCreate); - - /// - /// 仅创建新实例(不使用缓存) - /// IUiPageBehavior Create(string uiKey); - - /// - /// 预加载UI资源到缓存池 - /// - /// UI标识键 - /// 预加载数量,默认1个 - void Preload(string uiKey, int count = 1); - - /// - /// 批量预加载 - /// - void PreloadBatch(params string[] uiKeys); - - /// - /// 回收实例到缓存池 - /// - /// 要回收的页面实例 - void Recycle(IUiPageBehavior page); - - /// - /// 清理指定UI的缓存实例 - /// - void ClearCache(string uiKey); - - /// - /// 清理所有缓存 - /// - void ClearAllCache(); - - /// - /// 检查是否有缓存的实例 - /// - bool HasCached(string uiKey); - - #region 缓存策略管理 - - /// - /// 获取UI的缓存配置 - /// - /// UI标识符 - /// 缓存配置,如果未设置则返回默认配置 - UiCacheConfig GetCacheConfig(string uiKey); - - /// - /// 设置UI的缓存配置 - /// - /// UI标识符 - /// 缓存配置 - void SetCacheConfig(string uiKey, UiCacheConfig config); - - /// - /// 移除UI的缓存配置,恢复默认配置 - /// - /// UI标识符 - void RemoveCacheConfig(string uiKey); - - /// - /// 获取所有UI的缓存统计信息 - /// - /// 缓存统计字典 - IDictionary GetCacheStatistics(); - - #endregion } \ No newline at end of file diff --git a/GFramework.Game.Abstractions/ui/IUiRouter.cs b/GFramework.Game.Abstractions/ui/IUiRouter.cs index c8a07db..14b3556 100644 --- a/GFramework.Game.Abstractions/ui/IUiRouter.cs +++ b/GFramework.Game.Abstractions/ui/IUiRouter.cs @@ -25,9 +25,7 @@ public interface IUiRouter : ISystem /// UI界面的唯一标识符 /// 进入界面的参数,可为空 /// 界面切换策略,默认为Exclusive(独占) - /// 实例管理策略,默认为Reuse(复用) - void Push(string uiKey, IUiPageEnterParam? param = null, UiTransitionPolicy policy = UiTransitionPolicy.Exclusive, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse); + void Push(string uiKey, IUiPageEnterParam? param = null, UiTransitionPolicy policy = UiTransitionPolicy.Exclusive); /// @@ -54,13 +52,11 @@ public interface IUiRouter : ISystem /// 页面进入参数,可为空 /// 弹出页面时的销毁策略,默认为销毁 /// 推入页面时的过渡策略,默认为独占 - /// 实例管理策略 public void Replace( string uiKey, IUiPageEnterParam? param = null, UiPopPolicy popPolicy = UiPopPolicy.Destroy, - UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse); + UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive); /// /// 替换当前所有页面为已存在的页面(基于实例) @@ -146,12 +142,10 @@ public interface IUiRouter : ISystem /// 要显示的UI页面的唯一标识符 /// UI显示的层级,例如 Overlay、Modal 或 Toast /// 可选参数,用于传递给UI页面的初始化数据 - /// UI实例策略,默认为复用已存在的实例 void Show( string uiKey, UiLayer layer, - IUiPageEnterParam? param = null, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse); + IUiPageEnterParam? param = null); /// /// 在指定层级显示UI(Overlay / Modal / Toast等) diff --git a/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs b/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs deleted file mode 100644 index 1fbf7ff..0000000 --- a/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace GFramework.Game.Abstractions.ui; - -/// -/// UI页面实例管理策略(控制实例的生命周期) -/// -public enum UiInstancePolicy -{ - /// - /// 总是创建新实例 - /// - AlwaysCreate, - - /// - /// 复用已存在的实例(如果有) - /// - Reuse, - - /// - /// 从预加载池中获取或创建 - /// - Pooled -} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/ui/UiPopPolicy.cs b/GFramework.Game.Abstractions/ui/UiPopPolicy.cs index 6ba7e8c..9930d48 100644 --- a/GFramework.Game.Abstractions/ui/UiPopPolicy.cs +++ b/GFramework.Game.Abstractions/ui/UiPopPolicy.cs @@ -11,7 +11,7 @@ public enum UiPopPolicy Destroy, /// - /// 隐藏但保留实例(下次Push可复用) + /// 可恢复 /// - Cache + Suspend } \ No newline at end of file diff --git a/GFramework.Game/ui/UiRouterBase.cs b/GFramework.Game/ui/UiRouterBase.cs index ee6ce1d..e6ec761 100644 --- a/GFramework.Game/ui/UiRouterBase.cs +++ b/GFramework.Game/ui/UiRouterBase.cs @@ -76,10 +76,8 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter /// UI界面的唯一标识符 /// 进入界面的参数,可为空 /// 界面切换策略,默认为Exclusive(独占) - /// 实例管理策略,默认为Reuse(复用) public void Push(string uiKey, IUiPageEnterParam? param = null, - UiTransitionPolicy policy = UiTransitionPolicy.Exclusive, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse) + UiTransitionPolicy policy = UiTransitionPolicy.Exclusive) { if (IsTop(uiKey)) { @@ -90,12 +88,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter var @event = CreateEvent(uiKey, UiTransitionType.Push, policy, param); Log.Debug( - "Push UI Page: key={0}, policy={1}, instancePolicy={2}, stackBefore={3}", - uiKey, policy, instancePolicy, _stack.Count + "Push UI Page: key={0}, policy={1}, stackBefore={2}", + uiKey, policy, _stack.Count ); BeforeChange(@event); - DoPushPageInternal(uiKey, param, policy, instancePolicy); + DoPushPageInternal(uiKey, param, policy); AfterChange(@event); } @@ -176,18 +174,16 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter /// 页面进入参数,可为空 /// 弹出页面时的销毁策略,默认为销毁 /// 推入页面时的过渡策略,默认为独占 - /// 实例管理策略 public void Replace( string uiKey, IUiPageEnterParam? param = null, UiPopPolicy popPolicy = UiPopPolicy.Destroy, - UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse) + UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive) { var @event = CreateEvent(uiKey, UiTransitionType.Replace, pushPolicy, param); Log.Debug( - "Replace UI Stack with page: key={0}, popPolicy={1}, pushPolicy={2}, instancePolicy={3}", - uiKey, popPolicy, pushPolicy, instancePolicy + "Replace UI Stack with page: key={0}, popPolicy={1}, pushPolicy={2}", + uiKey, popPolicy, pushPolicy ); BeforeChange(@event); @@ -196,7 +192,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter DoClearInternal(popPolicy); // 使用工厂的增强方法获取实例 - var page = _factory.GetOrCreate(uiKey, instancePolicy); + var page = _factory.Create(uiKey); Log.Debug("Get/Create UI Page instance for Replace: {0}", page.GetType().Name); DoPushPageInternal(page, param, pushPolicy); @@ -366,8 +362,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter /// /// 执行Push页面的核心逻辑(基于 uiKey) /// - private void DoPushPageInternal(string uiKey, IUiPageEnterParam? param, UiTransitionPolicy policy, - UiInstancePolicy instancePolicy) + private void DoPushPageInternal(string uiKey, IUiPageEnterParam? param, UiTransitionPolicy policy) { // 执行进入守卫 if (!ExecuteEnterGuardsAsync(uiKey, param).GetAwaiter().GetResult()) @@ -377,7 +372,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter } // 使用工厂的增强方法获取实例 - var page = _factory.GetOrCreate(uiKey, instancePolicy); + var page = _factory.Create(uiKey); Log.Debug("Get/Create UI Page instance: {0}", page.GetType().Name); DoPushPageInternal(page, param, policy); @@ -397,7 +392,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter if (policy == UiTransitionPolicy.Exclusive) { - Log.Debug("Hide current page (Exclusive): {0}", current.View.GetType().Name); + Log.Debug("Suspend current page (Exclusive): {0}", current.View.GetType().Name); current.OnHide(); } } @@ -428,37 +423,26 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter return; var top = _stack.Pop(); + Log.Debug( "Pop UI Page internal: {0}, policy={1}, stackAfterPop={2}", top.GetType().Name, policy, _stack.Count ); - top.OnExit(); - if (policy == UiPopPolicy.Destroy) { - Log.Debug("Destroy UI Page: {0}", top.GetType().Name); + top.OnExit(); _uiRoot.RemoveUiPage(top); - // 不回收,直接销毁 } - else // UiPopPolicy.Cache + else // Suspend { - Log.Debug("Cache UI Page: {0}", top.GetType().Name); - _uiRoot.RemoveUiPage(top); - _factory.Recycle(top); // 回收到池中 + top.OnHide(); } - if (_stack.Count > 0) - { - var next = _stack.Peek(); - Log.Debug("Resume & Show page: {0}", next.GetType().Name); - next.OnResume(); - next.OnShow(); - } - else - { - Log.Debug("UI stack is now empty"); - } + if (_stack.Count <= 0) return; + var next = _stack.Peek(); + next.OnResume(); + next.OnShow(); } /// @@ -481,8 +465,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter public void Show( string uiKey, UiLayer layer, - IUiPageEnterParam? param = null, - UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse) + IUiPageEnterParam? param = null) { if (layer == UiLayer.Page) throw new ArgumentException("Use Push() for Page layer"); @@ -502,7 +485,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter } // 获取或创建实例 - var page = _factory.GetOrCreate(uiKey, instancePolicy); + var page = _factory.Create(uiKey); layerDict[uiKey] = page; // 添加到UiRoot,传入层级Z-order @@ -580,14 +563,11 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter { _uiRoot.RemoveUiPage(page); layerDict.Remove(uiKey); - Log.Debug("Hide & Destroy UI from layer: {0}, layer={1}", uiKey, layer); + Log.Debug("Suspend & Destroy UI from layer: {0}, layer={1}", uiKey, layer); } else { - _uiRoot.RemoveUiPage(page); - _factory.Recycle(page); - layerDict.Remove(uiKey); - Log.Debug("Hide & Cache UI from layer: {0}, layer={1}", uiKey, layer); + Log.Debug("Suspend & Suspend UI from layer: {0}, layer={1}", uiKey, layer); } } diff --git a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs b/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs index cc22bc3..e6a9e1d 100644 --- a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs +++ b/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs @@ -68,6 +68,11 @@ public class CanvasItemUiPageBehavior(T owner, string key) : IUiPageBehavior /// public void OnResume() { + if (owner.IsInvalidNode()) + { + return; + } + _page?.OnResume(); // 恢复节点的处理、物理处理和输入处理 diff --git a/GFramework.Godot/ui/GodotUiFactory.cs b/GFramework.Godot/ui/GodotUiFactory.cs index 5708532..5aeaf2a 100644 --- a/GFramework.Godot/ui/GodotUiFactory.cs +++ b/GFramework.Godot/ui/GodotUiFactory.cs @@ -2,435 +2,62 @@ using GFramework.Core.Abstractions.logging; using GFramework.Core.extensions; using GFramework.Core.logging; using GFramework.Core.utility; -using GFramework.Game.Abstractions.enums; using GFramework.Game.Abstractions.ui; -using GFramework.Godot.extensions; -using Godot; namespace GFramework.Godot.ui; /// -/// Godot UI工厂类,用于创建UI页面实例 -/// 继承自AbstractContextUtility并实现IUiFactory接口 +/// Godot UI工厂类,用于创建UI页面实例。 +/// 继承自AbstractContextUtility并实现IUiFactory接口。 /// public class GodotUiFactory : AbstractContextUtility, IUiFactory { - private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("GodotUiFactory"); + /// + /// 日志记录器,用于记录调试信息。 + /// + private static readonly ILogger Log = + LoggerFactoryResolver.Provider.CreateLogger("GodotUiFactory"); /// - /// LFU访问计数:instance -> 访问次数 + /// UI注册表,用于管理UI场景资源。 /// - private readonly Dictionary _accessCount = new(); - - /// - /// LRU访问时间队列:uiKey -> 按访问时间排序的实例列表 - /// - private readonly Dictionary> _accessTimeQueue = new(); - - /// - /// 追踪所有创建的实例(用于清理) - /// - private readonly Dictionary> _allInstances = new(); - - /// - /// 缓存配置:uiKey -> 配置 - /// - private readonly Dictionary _cacheConfigs = new(); - - /// - /// 缓存池:uiKey -> 实例队列 - /// - private readonly Dictionary> _cachedInstances = new(); - - /// - /// 缓存统计:uiKey -> 统计信息 - /// - private readonly Dictionary _cacheStatistics = new(); - private IGodotUiRegistry _registry = null!; /// - /// 创建或获取UI页面实例 - /// - public IUiPageBehavior GetOrCreate(string uiKey, UiInstancePolicy policy = UiInstancePolicy.AlwaysCreate) - { - return policy switch - { - UiInstancePolicy.Reuse => GetCachedOrCreate(uiKey), - UiInstancePolicy.Pooled => GetFromPoolOrCreate(uiKey), - _ => Create(uiKey) - }; - } - - /// - /// 仅创建新实例 + /// 根据指定的UI键创建UI页面实例。 /// + /// UI页面的唯一标识符。 + /// 返回创建的UI页面行为实例。 + /// + /// 当UI场景未实现IUiPageBehaviorProvider接口时抛出异常。 + /// public IUiPageBehavior Create(string uiKey) { + // 从注册表中获取指定UI键对应的场景 var scene = _registry.Get(uiKey); + + // 实例化场景节点 var node = scene.Instantiate(); + // 检查节点是否实现了IUiPageBehaviorProvider接口 if (node is not IUiPageBehaviorProvider provider) - throw new InvalidCastException($"UI scene {uiKey} must implement IUiPageBehaviorProvider"); + throw new InvalidCastException( + $"UI scene {uiKey} must implement IUiPageBehaviorProvider"); + // 获取页面行为实例 var page = provider.GetPage(); - // 追踪实例 - if (!_allInstances.ContainsKey(uiKey)) - _allInstances[uiKey] = new HashSet(); - _allInstances[uiKey].Add(page); - - Log.Debug("Created new UI instance: {0}", uiKey); + // 记录调试日志 + Log.Debug("Created UI instance: {0}", uiKey); return page; } /// - /// 预加载UI资源 + /// 初始化方法,在对象初始化时调用。 + /// 获取并设置UI注册表实例。 /// - public void Preload(string uiKey, int count = 1) - { - Log.Debug("Preloading UI: {0}, count={1}", uiKey, count); - - if (!_cachedInstances.ContainsKey(uiKey)) - _cachedInstances[uiKey] = new Queue(); - - var queue = _cachedInstances[uiKey]; - - for (var i = 0; i < count; i++) - { - var instance = Create(uiKey); - // 预加载的实例初始状态为隐藏 - instance.OnHide(); - queue.Enqueue(instance); - } - - Log.Debug("Preloaded {0} instances of {1}", count, uiKey); - } - - /// - /// 批量预加载 - /// - public void PreloadBatch(params string[] uiKeys) - { - foreach (var uiKey in uiKeys) Preload(uiKey); - } - - /// - /// 回收实例到缓存池 - /// - public void Recycle(IUiPageBehavior page) - { - var uiKey = page.Key; - - if (!_cachedInstances.ContainsKey(uiKey)) - _cachedInstances[uiKey] = new Queue(); - - // 确保实例处于隐藏状态 - page.OnHide(); - - // 更新统计信息 - UpdateStatisticsOnRecycle(uiKey); - - // 更新访问追踪 - UpdateAccessTracking(uiKey, page); - - _cachedInstances[uiKey].Enqueue(page); - Log.Debug("Recycled UI instance to pool: {0}, poolSize={1}", uiKey, _cachedInstances[uiKey].Count); - - // 检查是否需要淘汰 - CheckAndEvict(uiKey); - } - - /// - /// 获取UI的缓存配置 - /// - public UiCacheConfig GetCacheConfig(string uiKey) - { - return _cacheConfigs.TryGetValue(uiKey, out var config) ? config : UiCacheConfig.Default; - } - - /// - /// 设置UI的缓存配置 - /// - public void SetCacheConfig(string uiKey, UiCacheConfig config) - { - _cacheConfigs[uiKey] = config; - Log.Debug("Set cache config for UI: {0}, MaxSize={1}, Policy={2}", uiKey, config.MaxCacheSize, - config.EvictionPolicy); - - // 检查是否需要淘汰 - CheckAndEvict(uiKey); - } - - /// - /// 移除UI的缓存配置 - /// - public void RemoveCacheConfig(string uiKey) - { - if (_cacheConfigs.Remove(uiKey)) Log.Debug("Removed cache config for UI: {0}", uiKey); - } - - /// - /// 获取所有UI的缓存统计信息 - /// - public IDictionary GetCacheStatistics() - { - var result = new Dictionary(); - foreach (var kvp in _cacheStatistics) result[kvp.Key] = kvp.Value; - return result; - } - - /// - /// 清理指定UI的缓存 - /// - public void ClearCache(string uiKey) - { - if (!_cachedInstances.TryGetValue(uiKey, out var queue)) - return; - - var count = queue.Count; - while (queue.Count > 0) - { - var instance = queue.Dequeue(); - DestroyInstance(instance); - } - - _cachedInstances.Remove(uiKey); - Log.Debug("Cleared cache for UI: {0}, destroyed {1} instances", uiKey, count); - } - - /// - /// 清理所有缓存 - /// - public void ClearAllCache() - { - foreach (var uiKey in _cachedInstances.Keys) ClearCache(uiKey); - - Log.Debug("Cleared all UI caches"); - } - - /// - /// 检查是否有缓存的实例 - /// - public bool HasCached(string uiKey) - { - return _cachedInstances.TryGetValue(uiKey, out var queue) && queue.Count > 0; - } - protected override void OnInit() { _registry = this.GetUtility()!; } - - /// - /// 缓存统计信息实现类 - /// - private sealed class CacheStatisticsInfo : IUiCacheStatistics - { - public int CacheSize { get; set; } - public int HitCount { get; set; } - public int MissCount { get; set; } - public double HitRate => HitCount + MissCount > 0 ? (double)HitCount / (HitCount + MissCount) : 0; - public DateTime? LastAccessTime { get; set; } - } - - #region Private Methods - - /// - /// 获取缓存实例或创建新实例(Reuse策略) - /// - private IUiPageBehavior GetCachedOrCreate(string uiKey) - { - // 优先从缓存池获取 - if (_cachedInstances.TryGetValue(uiKey, out var queue) && queue.Count > 0) - { - var cached = queue.Dequeue(); - - // 更新统计:缓存命中 - UpdateStatisticsOnHit(uiKey); - - // 更新访问追踪 - UpdateAccessTracking(uiKey, cached); - - Log.Debug("Reused cached UI instance: {0}, remainingInPool={1}", uiKey, queue.Count); - return cached; - } - - // 没有缓存则创建新实例 - UpdateStatisticsOnMiss(uiKey); - Log.Debug("No cached instance, creating new: {0}", uiKey); - return Create(uiKey); - } - - /// - /// 从池中获取或创建(Pooled策略) - /// 如果池为空,自动创建并填充 - /// - private IUiPageBehavior GetFromPoolOrCreate(string uiKey) - { - // 如果池为空,先预加载一个 - if (HasCached(uiKey)) return GetCachedOrCreate(uiKey); - Log.Debug("Pool empty, preloading instance: {0}", uiKey); - Preload(uiKey); - - return GetCachedOrCreate(uiKey); - } - - /// - /// 销毁实例 - /// - private void DestroyInstance(IUiPageBehavior page) - { - var uiKey = page.Key; - - // 从追踪列表移除 - if (_allInstances.TryGetValue(uiKey, out var set)) set.Remove(page); - - // 从访问追踪移除 - _accessCount.Remove(page); - if (_accessTimeQueue.TryGetValue(uiKey, out var queue)) queue.RemoveAll(x => x.instance == page); - - // 销毁Godot节点 - if (page.View is Node node) node.QueueFreeX(); - } - - /// - /// 更新统计信息:回收 - /// - private void UpdateStatisticsOnRecycle(string uiKey) - { - if (!_cacheStatistics.ContainsKey(uiKey)) - _cacheStatistics[uiKey] = new CacheStatisticsInfo(); - - var stats = _cacheStatistics[uiKey]; - stats.CacheSize = _cachedInstances[uiKey].Count + 1; - stats.LastAccessTime = DateTime.Now; - } - - /// - /// 更新统计信息:命中 - /// - private void UpdateStatisticsOnHit(string uiKey) - { - if (!_cacheStatistics.ContainsKey(uiKey)) - _cacheStatistics[uiKey] = new CacheStatisticsInfo(); - - var stats = _cacheStatistics[uiKey]; - stats.HitCount++; - stats.CacheSize = _cachedInstances[uiKey].Count; - stats.LastAccessTime = DateTime.Now; - } - - /// - /// 更新统计信息:未命中 - /// - private void UpdateStatisticsOnMiss(string uiKey) - { - if (!_cacheStatistics.ContainsKey(uiKey)) - _cacheStatistics[uiKey] = new CacheStatisticsInfo(); - - var stats = _cacheStatistics[uiKey]; - stats.MissCount++; - stats.CacheSize = _cachedInstances.TryGetValue(uiKey, out var queue) ? queue.Count : 0; - } - - /// - /// 更新访问追踪 - /// - private void UpdateAccessTracking(string uiKey, IUiPageBehavior instance) - { - var now = DateTime.Now; - - // LRU: 更新访问时间队列 - if (!_accessTimeQueue.ContainsKey(uiKey)) - _accessTimeQueue[uiKey] = new List<(IUiPageBehavior, DateTime)>(); - - var timeQueue = _accessTimeQueue[uiKey]; - timeQueue.RemoveAll(x => x.instance == instance); - timeQueue.Add((instance, now)); - - // LFU: 更新访问计数 - _accessCount.TryGetValue(instance, out var count); - _accessCount[instance] = count + 1; - } - - /// - /// 检查并执行淘汰 - /// - private void CheckAndEvict(string uiKey) - { - var config = GetCacheConfig(uiKey); - var currentSize = _cachedInstances.TryGetValue(uiKey, out var queue) ? queue.Count : 0; - - if (currentSize <= config.MaxCacheSize) return; - var toEvict = currentSize - config.MaxCacheSize; - - for (var i = 0; i < toEvict; i++) - if (config.EvictionPolicy == CacheEvictionPolicy.Lru) - EvictLru(uiKey); - else - EvictLfu(uiKey); - - Log.Debug("Evicted {0} instances for UI: {1}", toEvict, uiKey); - } - - /// - /// LRU淘汰策略 - /// - private void EvictLru(string uiKey) - { - if (!_accessTimeQueue.TryGetValue(uiKey, out var timeQueue) || timeQueue.Count == 0) - return; - - var oldest = timeQueue.OrderBy(x => x.accessTime).First(); - - // 从队列中移除 - if (_cachedInstances.TryGetValue(uiKey, out var queue)) - { - var tempQueue = new Queue(); - var removed = false; - - while (queue.Count > 0) - { - var item = queue.Dequeue(); - if (!removed && item == oldest.instance) - { - DestroyInstance(item); - removed = true; - } - else - { - tempQueue.Enqueue(item); - } - } - - // 重新填充队列 - while (tempQueue.Count > 0) - queue.Enqueue(tempQueue.Dequeue()); - } - } - - /// - /// LFU淘汰策略 - /// - private void EvictLfu(string uiKey) - { - if (!_cachedInstances.TryGetValue(uiKey, out var queue) || queue.Count == 0) - return; - - // 找到访问次数最少的实例 - IUiPageBehavior? toRemove = null; - var minCount = int.MaxValue; - - foreach (var instance in queue) - if (_accessCount.TryGetValue(instance, out var count) && count < minCount) - { - minCount = count; - toRemove = instance; - } - - if (toRemove != null) DestroyInstance(toRemove); - } - - #endregion } \ No newline at end of file