From 58baa6269e93664e6aa3357c4da510610db4b4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Thu, 26 Mar 2026 13:51:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/Core/ServiceContractUtility.cs | 37 ++++++++++++++----- .../ABase/Base/Service/Core/ServiceScope.cs | 14 ++++++- .../Service/Unity/MonoServiceBehaviour.cs | 8 ++-- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/Runtime/ABase/Base/Service/Core/ServiceContractUtility.cs b/Runtime/ABase/Base/Service/Core/ServiceContractUtility.cs index dddf4e9..18a29aa 100644 --- a/Runtime/ABase/Base/Service/Core/ServiceContractUtility.cs +++ b/Runtime/ABase/Base/Service/Core/ServiceContractUtility.cs @@ -15,7 +15,34 @@ namespace AlicizaX typeof(IServiceOrder), }; + // Cache for the common no-extraContracts path — contracts per concrete type never change. + private static readonly Dictionary> _contractCache = new Dictionary>(); + public static List Collect(Type serviceType, IReadOnlyList extraContracts) + { + if (extraContracts == null || extraContracts.Count == 0) + { + if (_contractCache.TryGetValue(serviceType, out var cached)) + return cached; + var result = BuildContracts(serviceType); + _contractCache[serviceType] = result; + return result; + } + + // Extra contracts path: build fresh, validate, append extras. + var contracts = BuildContracts(serviceType); + contracts = new List(contracts); // don't mutate the cached list + var unique = new HashSet(contracts); + for (var i = 0; i < extraContracts.Count; i++) + { + var extraContract = extraContracts[i]; + ValidateExtraContract(serviceType, extraContract); + if (unique.Add(extraContract)) contracts.Add(extraContract); + } + return contracts; + } + + private static List BuildContracts(Type serviceType) { var contracts = new List { serviceType }; var unique = new HashSet { serviceType }; @@ -28,16 +55,6 @@ namespace AlicizaX if (ExcludedContracts.Contains(contract)) continue; if (unique.Add(contract)) contracts.Add(contract); } - - if (extraContracts == null) return contracts; - - for (var i = 0; i < extraContracts.Count; i++) - { - var extraContract = extraContracts[i]; - ValidateExtraContract(serviceType, extraContract); - if (unique.Add(extraContract)) contracts.Add(extraContract); - } - return contracts; } diff --git a/Runtime/ABase/Base/Service/Core/ServiceScope.cs b/Runtime/ABase/Base/Service/Core/ServiceScope.cs index 595dcd1..c13878a 100644 --- a/Runtime/ABase/Base/Service/Core/ServiceScope.cs +++ b/Runtime/ABase/Base/Service/Core/ServiceScope.cs @@ -154,10 +154,11 @@ namespace AlicizaX var service = snapshot[i]; if (!_contractsByService.ContainsKey(service)) continue; RemoveFromLifecycleLists(service); - RemoveBindings(service); + RemoveContractBindings(service); // skip _registrationOrder.Remove — we clear below service.Destroy(); } + _registrationOrder.Clear(); IsDisposed = true; } @@ -206,6 +207,17 @@ namespace AlicizaX _registrationOrder.Remove(service); } + // Used during full Dispose — skips the O(n) _registrationOrder.Remove since we clear the list afterwards. + private void RemoveContractBindings(IService service) + { + if (_contractsByService.TryGetValue(service, out var contracts)) + { + for (var i = 0; i < contracts.Count; i++) + _servicesByContract.Remove(contracts[i]); + } + _contractsByService.Remove(service); + } + private IServiceTickable[] GetTickSnapshot() { if (_tickablesDirty) diff --git a/Runtime/ABase/Base/Service/Unity/MonoServiceBehaviour.cs b/Runtime/ABase/Base/Service/Unity/MonoServiceBehaviour.cs index 6d4e339..7e8d123 100644 --- a/Runtime/ABase/Base/Service/Unity/MonoServiceBehaviour.cs +++ b/Runtime/ABase/Base/Service/Unity/MonoServiceBehaviour.cs @@ -61,9 +61,6 @@ namespace AlicizaX // DefaultExecutionOrder 会影响所有生命周期(含 Awake),用 Start 可彻底规避执行顺序陷阱。 private void Awake() { - if (_dontDestroyOnLoad) - DontDestroyOnLoad(gameObject); - OnAwake(); } @@ -78,6 +75,11 @@ namespace AlicizaX return; } + // Defer DontDestroyOnLoad until after the duplicate check so rejected + // duplicates are never moved to the DontDestroyOnLoad scene. + if (_dontDestroyOnLoad) + DontDestroyOnLoad(gameObject); + scope.Register(this); }