com.alicizax.unity.framework/Runtime/ABase/Event/EventContainer.cs
b4lie 93aba55cb0 增加Event调试编辑器
1.增加Event调试编辑器查看订阅信息
2.调试编辑器增加快照查看泄露风险数据
2026-03-21 21:19:07 +08:00

334 lines
10 KiB
C#

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.IL2CPP.CompilerServices;
using UnityEngine;
namespace AlicizaX
{
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
public static class EventContainer<TPayload> where TPayload : struct, IEventArgs
{
private static readonly int InitialSize = EventInitialSize<TPayload>.Size;
private static readonly int TypeId;
private static Action<TPayload>[] _callbacks;
private static int[] _versions;
private static int[] _activeSlots;
private static int[] _freeSlots;
private static int _freeCount;
private static int[] _activeIndices;
private static int _activeCount;
private static int _version;
#if UNITY_EDITOR
private static int _publishDepth;
#endif
#if Event_StrictCheck
private static System.Collections.Generic.HashSet<Action<TPayload>> _activeHandlers;
#endif
static EventContainer()
{
TypeId = UnsubscribeRegistry.Register(Unsubscribe);
_callbacks = new Action<TPayload>[InitialSize];
_versions = new int[InitialSize];
_activeSlots = new int[InitialSize];
_freeSlots = new int[InitialSize];
_activeIndices = new int[InitialSize];
_freeCount = InitialSize;
for (int i = 0; i < InitialSize; i++)
{
_freeSlots[i] = i;
}
#if Event_StrictCheck
_activeHandlers = new System.Collections.Generic.HashSet<Action<TPayload>>();
#endif
#if UNITY_EDITOR
EventDebugRegistry.RegisterContainer<TPayload>(GetDebugSubscriberCount, GetDebugCapacity, GetDebugSubscribers);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EventRuntimeHandle Subscribe(Action<TPayload> callback)
{
#if UNITY_EDITOR
ThrowIfMutatingDuringPublish("subscribe");
#endif
#if Event_StrictCheck
if (_activeHandlers.Contains(callback))
{
UnityEngine.Debug.LogWarning($"重复订阅事件处理程序: {callback.Method.Name}");
return default;
}
_activeHandlers.Add(callback);
#endif
int handlerIndex = GetFreeSlot();
if (_activeCount >= _activeIndices.Length)
{
Array.Resize(ref _activeIndices, _activeIndices.Length << 1);
}
int activeIndex = _activeCount++;
_activeIndices[activeIndex] = handlerIndex;
int version = ++_version;
_callbacks[handlerIndex] = callback;
_versions[handlerIndex] = version;
_activeSlots[handlerIndex] = activeIndex;
#if UNITY_EDITOR
EventDebugRegistry.RecordSubscribe<TPayload>(_activeCount, _callbacks.Length);
#endif
return new EventRuntimeHandle(TypeId, handlerIndex, version);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetFreeSlot()
{
if (_freeCount > 0)
{
return _freeSlots[--_freeCount];
}
int oldLen = _callbacks.Length;
int newSize = oldLen == 0 ? 64 : oldLen << 1;
Array.Resize(ref _callbacks, newSize);
Array.Resize(ref _versions, newSize);
Array.Resize(ref _activeSlots, newSize);
Array.Resize(ref _freeSlots, newSize);
for (int i = newSize - 1; i >= oldLen; i--)
{
_freeSlots[_freeCount++] = i;
}
#if UNITY_EDITOR
EventDebugRegistry.RecordResize<TPayload>(_activeCount, _callbacks.Length);
#endif
return _freeSlots[--_freeCount];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Unsubscribe(int handlerIndex, int version)
{
#if UNITY_EDITOR
ThrowIfMutatingDuringPublish("unsubscribe");
#endif
if (_versions[handlerIndex] != version) return;
int lastActiveIndex = --_activeCount;
int lastHandlerIndex = _activeIndices[lastActiveIndex];
int currentActiveIndex = _activeSlots[handlerIndex];
_activeIndices[currentActiveIndex] = lastHandlerIndex;
_activeSlots[lastHandlerIndex] = currentActiveIndex;
#if Event_StrictCheck
_activeHandlers.Remove(_callbacks[handlerIndex]);
#endif
_callbacks[handlerIndex] = null;
_versions[handlerIndex] = 0;
if (_freeCount >= _freeSlots.Length)
{
Array.Resize(ref _freeSlots, _freeSlots.Length << 1);
}
_freeSlots[_freeCount++] = handlerIndex;
#if UNITY_EDITOR
EventDebugRegistry.RecordUnsubscribe<TPayload>(_activeCount, _callbacks.Length);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Publish(in TPayload payload)
{
#if UNITY_EDITOR
_publishDepth++;
try
{
#endif
int count = _activeCount;
#if UNITY_EDITOR
EventDebugRegistry.RecordPublish<TPayload>(count, _callbacks.Length);
#endif
if (count == 0) return;
int[] indices = _activeIndices;
Action<TPayload>[] callbacks = _callbacks;
int i = 0;
int unrolled = count & ~3; // count - (count % 4)
for (; i < unrolled; i += 4)
{
int idx0 = indices[i];
int idx1 = indices[i + 1];
int idx2 = indices[i + 2];
int idx3 = indices[i + 3];
callbacks[idx0](payload);
callbacks[idx1](payload);
callbacks[idx2](payload);
callbacks[idx3](payload);
}
switch (count - i)
{
case 3:
callbacks[indices[i]](payload);
callbacks[indices[i + 1]](payload);
callbacks[indices[i + 2]](payload);
break;
case 2:
callbacks[indices[i]](payload);
callbacks[indices[i + 1]](payload);
break;
case 1:
callbacks[indices[i]](payload);
break;
}
#if UNITY_EDITOR
}
finally
{
_publishDepth--;
}
#endif
}
public static int SubscriberCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _activeCount;
}
public static void EnsureCapacity(int capacity)
{
#if UNITY_EDITOR
ThrowIfMutatingDuringPublish("ensure capacity");
#endif
if (_callbacks.Length >= capacity) return;
int oldLen = _callbacks.Length;
Array.Resize(ref _callbacks, capacity);
Array.Resize(ref _versions, capacity);
Array.Resize(ref _activeSlots, capacity);
Array.Resize(ref _freeSlots, capacity);
Array.Resize(ref _activeIndices, capacity);
for (int i = capacity - 1; i >= oldLen; i--)
{
_freeSlots[_freeCount++] = i;
}
#if UNITY_EDITOR
EventDebugRegistry.RecordResize<TPayload>(_activeCount, _callbacks.Length);
#endif
}
public static void Clear()
{
#if UNITY_EDITOR
ThrowIfMutatingDuringPublish("clear");
#endif
for (int i = 0; i < _activeCount; i++)
{
int idx = _activeIndices[i];
_callbacks[idx] = null;
_versions[idx] = 0;
_freeSlots[_freeCount++] = idx;
}
_activeCount = 0;
#if UNITY_EDITOR
EventDebugRegistry.RecordClear<TPayload>(_activeCount, _callbacks.Length);
#endif
#if Event_StrictCheck
_activeHandlers.Clear();
#endif
}
#if UNITY_EDITOR
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ThrowIfMutatingDuringPublish(string operation)
{
if (_publishDepth <= 0) return;
throw new InvalidOperationException(
$"EventContainer<{typeof(TPayload).Name}> cannot {operation} while publishing. " +
"Supporting dispatch-time mutations would require slower publish semantics.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetDebugSubscriberCount() => _activeCount;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetDebugCapacity() => _callbacks.Length;
private static EventDebugSubscriberInfo[] GetDebugSubscribers()
{
int count = _activeCount;
if (count == 0)
{
return Array.Empty<EventDebugSubscriberInfo>();
}
EventDebugSubscriberInfo[] subscribers = new EventDebugSubscriberInfo[count];
for (int i = 0; i < count; i++)
{
int handlerIndex = _activeIndices[i];
Action<TPayload> callback = _callbacks[handlerIndex];
object target = callback.Target;
bool isStatic = target == null;
bool isUnityObjectDestroyed = false;
UnityEngine.Object unityTarget = null;
if (!isStatic && target is UnityEngine.Object engineObject)
{
unityTarget = engineObject;
isUnityObjectDestroyed = engineObject == null;
}
subscribers[i] = new EventDebugSubscriberInfo(
handlerIndex,
_versions[handlerIndex],
callback.Method.DeclaringType?.FullName ?? "<UnknownType>",
callback.Method.Name,
target?.GetType().FullName ?? "<Static>",
unityTarget,
isStatic,
isUnityObjectDestroyed);
}
return subscribers;
}
#endif
}
}