Evnet Manager2

WeakReference를 사용해서 구독자가 파괴되면 자동으로 GC가 수거 되는 EventBus를 만들어 보겠습니다.
구독자(this), 메서드를 묶어서 관리 합니다.
Publish시 살아있는 구독자만 호출 합니다.

1. EventBus 구현

SafeEventBus.cs 파일
using System;
using System.Collections.Generic;
using UnityEngine;

public static class SafeEventBus
{
    private class Subscription
    {
        public WeakReference TargetRef;  // 구독자 객체 (MonoBehaviour)
        public Delegate Callback;        // Action<T>
    }

    private static readonly Dictionary<Type, List<Subscription>> events = new();

    public static void Subscribe<T>(MonoBehaviour subscriber, Action<T> callback)
    {
        if (subscriber == null || callback == null)
            throw new ArgumentNullException();

        var type = typeof(T);

        if (!events.TryGetValue(type, out var list))
        {
            list = new List<Subscription>();
            events[type] = list;
        }

        list.Add(new Subscription
        {
            TargetRef = new WeakReference(subscriber),
            Callback = callback
        });
    }

    // 이벤트 발행
    public static void Publish<T>(T evt)
    {
        var type = typeof(T);
        if (!events.TryGetValue(type, out var list)) return;

        // 살아있는 구독자만 호출
        for (int i = list.Count - 1; i >= 0; i--)
        {
            var sub = list[i];
            if (sub.TargetRef.IsAlive)
            {
                ((Action<T>)sub.Callback)?.Invoke(evt);
            }
            else
            {
                // 이미 파괴된 구독자는 제거
                list.RemoveAt(i);
            }
        }

        // 리스트가 비었으면 타입 제거
        if (list.Count == 0)
            events.Remove(type);
    }

    // 명시적 해제 (선택)
    public static void Unsubscribe<T>(MonoBehaviour subscriber, Action<T> callback)
    {
        if (subscriber == null || callback == null) return;

        var type = typeof(T);
        if (!events.TryGetValue(type, out var list)) return;

        list.RemoveAll(sub => (object)sub.TargetRef.Target == subscriber &&  sub.Callback == (Delegate)callback);

        if (list.Count == 0)
            events.Remove(type);
    }
}


2. Event 정의

PlayerScoreEvent.cs 파일
public class PlayerScoredEvent
{
    public int Score { get; private set; }

    public PlayerScoredEvent(int score)
    {
        Score = score;
    }
}

3. 이벤트 구독

ScoreManager.cs 파일
using UnityEngine;

public class ScoreManager : MonoBehaviour
{
    void OnEnable()
    {
        SafeEventBus.Subscribe<PlayerScoredEvent>(this, OnPlayerScored);
    }

    void OnDisable()
    {
        SafeEventBus.Unsubscribe<PlayerScoredEvent>(this,OnPlayerScored);
    }

    void OnPlayerScored(PlayerScoredEvent e)
    {
        Debug.Log("SafeEventBus: Score: " + e.Score);
    }
}

4. 이벤트 발행

EventBus.Publish(new PlayerScoredEvent(10));

✅ 특징:

  • ScoreManager가 파괴되면 WeakReference 때문에 자동으로 이벤트 리스트에서 제거됨 → 메모리 누수 없음

  • OnDisable에서 Unsubscribe 안 해도 안전

  • Action 기반 그대로 사용 가능 → 타입 안전성 유지