[์๋ก ]
์๋ ?
์ค๋์ C#์์ ์ด๊ฐ๋จ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์ฝ๋๋ฅผ ๊ณต์ ํด๋ณผ๊น ํด.
Unity ๋ฟ๋ง ์๋๋ผ C#์ ์ฌ์ฉํ๋ ๊ณณ์ด๋ฉด ์ด๋๋ ์ฌ์ฉ ๊ฐ๋ฅํจ.
์ด๊ฒ์ ์ด์ฉํ๋ฉด ๋ฐ์ดํฐ๊ฐ ๋ฐ๋ ๋๋ง๋ค ๊ท์ฐฎ๊ฒ ๋ฐ๋ก UI์ ๊ฐ์ ์ค์ ํ๊ฑฐ๋ ํธ์ถํด์ฃผ๋ ๋ฒ๊ฑฐ๋ก์์ ์ ๊ฑฐํ ์ ์์ด.
์ฆ, ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ฉด ๊ทธ์ ๊ด๋ จ๋ ์ด๋ฒคํธ๋ค์ด ์๋์ผ๋ก ์ํ๋๋๊ฑฐ์ง.
์๋ฅผ ๋ค์ด์ ๊ฒ์ ์ค์ ์ ์ ํด์ณ์ Score(์ ์)๊ฐ ์ฌ๋ผ๊ฐ ๋ ๋ณดํต์
Score += 10;
ScoreLabel.text = $"์ ์: {Score}";
๋ญ ์ด๋ฐ์์ผ๋ก ํ์์?
์ง๊ธ์ ํฐ ๋ฌธ์ ์์ด๋ณด์ด์ง๋ง ๋์ค์ ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ณ ๋ณต์กํด์ง๋ค ๋ณด๋ฉด ๊ฒฐ๊ตญ์ ๋ฐ์ดํฐ๋ ๋ฐ์ดํฐ๋๋ก, UI๋ UI๋๋ก ๊ด๋ฆฌํด์ผ ํ ํ์์ฑ์ด ์๊ธธ๊ฑฐ์ผ.
๊ทธ๋ผ ๊ทธ ๋ ์ ๋์ ๋๋๋ค๋ณด๋ฉด ์ฌ๋ฌ ์๋ก์ฌํญ์ด ์๊ฒจ. Score๋ฅผ ๊ด๋ฆฌํ๋ A Class์ UI๋ฅผ ๊ด๋ฆฌํ๋ B Class๊ฐ ์๋ค๋ฉด, ์ด A Class๊ฐ B Class๋ฅผ ์ฐธ์กฐํด์
B b = somewhere.getB();
b.ScoreLabel.text = $"์ ์: {Score}";
๋ญ ์ด๋ฐ์์ผ๋ก ๋๋๋ ์๋ฏธ๊ฐ ์์ด์ง ์๋ ์๋ค๋๊ฑฐ์ง. ๊ฒฐ๊ตญ ๋๊ฐ์ด ๋ณต์กํด์ง๋๊น.
์ฌํผ ์ด๊ธฐ์ ์ค์ ๋ง ์ํด ๋๋ฉด ์ ๋ ์ฝ๋ ์์ญ๋ค์ด ์๋ก ๊ฐ์ญํ ํ์๊ฐ ์์ด์ง๋๊ฒ ๋ฐ๋ก ๋ฐ์ธ๋ฉ์ ํ๋ ๊ฐ์ฅ ํฐ ์ด์ ๊ฐ ์๋๊น ์ถ์ด.(๋ญ ๋ฐ์ธ๋ฉ ๋ง๊ณ ๋ ์ฌ๋ฌ ๊ธฐ๋ฒ๋ค์ด ์์ ์ ์๊ฒ ์ง๋ง... )
[๋ณธ๋ก ]
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๊ณ , Github์๋ ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ง์ด ์์ด. ๋๋ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ฉด ๊ด๋ จ๋ ์ด๋ฒคํธ๋ค์ด ๋์ํ๋ ๊ฐ๋จํ ๊ธฐ๋ฅ๋ง ์ํ๊ธฐ์ ์ง์ ๊ตฌํํด๋ดค์ด.
C#์์๋ ์ด๋ฅผ ์ํด ๋ฌธ๋ฒ์ ์ผ๋ก delegate ์ event ํค์๋๋ฅผ ์ ๊ณตํ๊ณ , ๋๋ ๊ฑฐ๊ธฐ์ ์ข ๋ ์์ ์ฑ์ ์ถ๊ตฌํด์ WeakReference Class๋ ์ฌ์ฉํ์ด.
WeakReference๋ ์ฝํ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ GC์ ์ํด ์ฐธ์กฐํ๊ณ ์๋ ๊ฐ์ฒด๊ฐ ํด์ ๋๋ ํน์ฑ์ด ์์ด์ ๋ฉ๋ชจ๋ฆฌ ๋์๋ก๋ถํฐ ์์ ํ ์ ์์ด.
์ฌํผ ์๋ ๊ตฌํ ์ฝ๋๊ฐ ์์ผ๋๊น CCCV ํด์ ์ฌ์ฉํ๋ฉด ๋ผ.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Collections.Immutable;
internal class TGDataBinder<A> : IDisposable, IComparable<TGDataBinder<A>>, IEquatable<TGDataBinder<A>> where A : IComparable<A> {
private sealed class WeakEventManager<T> {
// ๋ฑ๋กํ ์ฝ๋ฐฑํจ์๊ฐ ์ ์ฅ๋๋ ๋ณ์
private ImmutableList<WeakReference<Action<T>>> handlers = ImmutableList<WeakReference<Action<T>>>.Empty;
// lambda ํจ์๊ฐ ๋ฐ๋ก GC ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํ ์ฅ์น
private readonly ConditionalWeakTable<object, List<object>> delegateKeepAlive = new();
public void AddHandler(Action<T> handler) {
if (handler == null)
return;
var weakReference = new WeakReference<Action<T>>(handler);
handlers = handlers.Add(weakReference);
if (handler.Target != null) {
delegateKeepAlive.GetOrCreateValue(handler.Target).Add(handler);
}
}
public void RemoveHandler(Action<T> handler) {
if (handler == null)
return;
handlers = handlers.RemoveAll(wr => !wr.TryGetTarget(out var target) || handler.Equals(target));
if (handler.Target != null && delegateKeepAlive.TryGetValue(handler.Target, out var weakReference)) {
weakReference.Remove(handler);
}
}
public void Raise(T data) {
foreach (var maybeHandler in handlers) {
if (maybeHandler.TryGetTarget(out var handler)) {
handler(data);
} else {
handlers = handlers.Remove(maybeHandler);
}
}
}
public void ClearHandlers() {
handlers.Clear();
delegateKeepAlive.Clear();
}
}
private A data;
private readonly WeakEventManager<A> weakEventManager;
// ์ด event ํ๋กํผํฐ๋ฅผ ํตํด์๋ ์ฝ๋ฐฑํจ์ ์ถ๊ฐ or ์ญ์ ๊ฐ๋ฅ
public event Action<A> OnDataChanged {
add => weakEventManager.AddHandler(value);
remove => weakEventManager.RemoveHandler(value);
}
// ์ค์ ๊ด๋ฆฌํ ๋ฐ์ดํฐ ํ๋กํผํฐ
public A Data {
get => data;
set {
data = value;
weakEventManager.Raise(data); // <- ์ด๋ถ๋ถ์ด ์ค์ ๋ฐ์ธ๋ฉ ๋๋ ์ฝ๋
}
}
public TGDataBinder() { weakEventManager = new(); }
// ์ํ๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ๋ฑ๋ก
public void AddHandler(Action<A> handler) {
OnDataChanged += handler;
}
// ์๋์ผ๋ก ํด์ ํ ๋ ํธ์ถ
public void Dispose() {
weakEventManager.ClearHandlers();
}
public int CompareTo(TGDataBinder<A> obj) => Data.CompareTo(obj.Data);
public bool Equals(TGDataBinder<A> other) => Data.Equals(other.Data);
public override int GetHashCode() => Data.GetHashCode();
public override bool Equals(object obj) {
if (!(obj is TGDataBinder<A>)) {
return false;
} else {
return Equals((TGDataBinder<A>)obj);
}
}
public static bool operator >(TGDataBinder<A> n1, TGDataBinder<A> n2) => n1.CompareTo(n2) > 0;
public static bool operator <(TGDataBinder<A> n1, TGDataBinder<A> n2) => n1.CompareTo(n2) < 0;
public static bool operator ==(TGDataBinder<A> n1, TGDataBinder<A> n2) => n1.Equals(n2);
public static bool operator !=(TGDataBinder<A> n1, TGDataBinder<A> n2) => !(n1 == n2);
}
๋นจ๊ฐ์ ๊ธ์๋ ์๋ง ๊ธฐ๋ณธ ํจํค์ง์์ ์ง์ ์ํ ์ ์์ผ๋ ๊ธฐ๋ณธ List ์๋ฃ๊ตฌ์กฐ๋ก ๋ฐ๊พธ๊ฑฐ๋ ์๋๋ฉด Nuget ์ ํตํด์ ์ค์นํ๋ฉด๋ผ.
ํ๋์ ๊ธ์๋ ์ ํ์ฌํญ์ด๋ผ ์ํ์ง ์์ผ๋ฉด ์ญ์ ํด๋ ๋ฌด๋ฐฉํด.
์งํ ๊ธ์๋ ์ค์ ์ฌ์ฉ๋๋ ์์ฑ์, ํ๋กํผํฐ, ๋ฉ์๋์ผ. ํด๋์ค ์ด๋ฆ๋ ์ํ๋ ๊ฑธ๋ก ๋ฐ๊ฟ์ ์ฌ์ฉํ๋ฉด๋ผ.
์ด์ ์ฌ์ฉ์๋ฅผ ๋ณด์.
์ด๋ ๊ฒ PlayerRepositoryํด๋์ค์ ๋ฉค๋ฒ๋ณ์๋ก ์ ์ธ ๋ฐ ์์ฑํด์ฃผ๊ณ ,
UIManagerํด๋์ค์์๋ UI Text ๋ณ๊ฒฝ์ฉ ๋ฉ์๋๋ฅผ ์์ฑํด์ฃผ๊ณ .
GameManagerํด๋์ค์์ PlayerRepository์ UIManager๋ฅผ ์ฐธ์กฐํ๊ณ PlayerRepository์ ๊ฐ ๋ณ์ ๋ด๋ถ์ ๋ฐ์ดํฐ๋ค์ด ๋ฐ๋ ๋๋ง๋ค ํธ์ถํด์ค ์ฝ๋ฐฑํจ์๋ค์ UIManager๋ก๋ถํฐ ๊ฐ์ ธ์์ ์ง์ ํด์ฃผ์์ด.
PlayerRepository๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ GameManagerํด๋์ค๋ ํน์ ๋ค๋ฅธ ํด๋์ค์์ AddScore๊ฐ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด, ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํจ๊ณผ ๋์์ ๋ด๋ถ์ ์ผ๋ก ๋ฑ๋ก๋ ์ฝ๋ฐฑํจ์๋ฅผ ํธ์ถํด์ UI๋ฅผ ๋ณ๊ฒฝํ ๊ฑฐ์ผ. ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ค๊ณ ํด์ UIManager์ SetScore() ๋ฉ์๋๋ฅผ ๋ฐ๋ก ํธ์ถํด์ค ํ์๊ฐ ์๋จ ์๋ฆฌ์ง.
์ฆ, PlayerRepository์ UIManager๋ ์๋ก์ ๊ด๊ณ๋ก๋ถํฐ ๋ถ๋ฆฌ๋์๋ค๊ณ ๋ณผ ์ ์์ง.
PlayerRepository ์ ๊ทธ ๋ด๋ถ์ ์๋ TGDataBinder ๋ชจ๋ UIManager๊ฐ ๋ฌด์จ์ผ์ ํ๋์ง ์ ํ์๊ฐ ์๋ค๋ ์๋ฆฌ์ผ. ๊ทธ์ ์์ ํํ ๋ฑ๋ก๋ ์ฝ๋ฐฑํจ์๋ง ํธ์ถํด์ฃผ๋ฉด๋๋๊น. ๋ํ UIManager๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ธ๋ถ ๋ฐ์ดํฐ๋ค์ด ์ด๋ค์์ผ๋ก ๊ด๋ฆฌ๋๋์ง ์ ํ์ ์๊ณ .
๊ทธ๋ฆฌ๊ณ ๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฒ์ฌ๋ฅผ ์ํด ํ๋กํ์ผ๋ฌ๋ก ํ ์คํธ๋ฅผ ํด๋ดค์ด.
์ ์ค์ท์ ์ ๋ํฐ Memory Profiler๋ก TGDataBinder์ ๊ฐ์ ๋ถํ๋ฅผ ์ฃผ๊ณ Dispose ์์ผฐ์ ๋ ๋ด๋ถ์ ์์ ์ ๋๋ก ๋ฉ๋ชจ๋ฆฌ ํด์ ๊ฐ ๋๋์ง ๊ฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์บก์ณ ํ ํ ๋น๊ต ํ ์คํธ ํด๋ณธ ๊ฒฐ๊ณผ์ผ.(A๋ 100001๊ฐ์์ ๋ ์บก์ณํ ๋ฉ๋ชจ๋ฆฌ๊ณ , B๋ Dispose ํ ์บก์ณํ ๋ฉ๋ชจ๋ฆฌ์ผ)
์์ฑ๋ ์ฝ๋ฐฑํจ์๊ฐ ์ด 100001์ธ๋ฐ ์ด๊ฑด TGDataBinder ์์ฑํ๋ฉด์ ์ต์ด ์ฝ๋ฐฑํจ์ 1๊ฐ ์ถ๊ฐํ๊ณ , ์ดํ 10๋ง๊ฐ๋ฅผ (ํ ์คํธํ๊ณ ์) ์ผ๋ถ๋ฌ ๋ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ ํ์๋๊ฑฐ์ผ.
์ฆ, ์ ๋๋ก ๋์ํ๋ค๋ ๊ฑธ ์ ์ ์์ง. DI์์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ํ๋ฉด Release๋ Dispose ๋๋ ์์ ์ ๊ฐ์ด Dispose ๋ฉ์๋ ํธ์ถํด์ฃผ๋ฉด ๋๊ณ , ๊ตณ์ด ๊ด๋ฆฌ ์ํ๋๋ผ๋ GC๊ฐ ํธ์ถํ๋ ์์ ์ ํด๋น DataBinder๋ ๊ฐ์ด ์ ๋ฆฌ๋ ๊ฑฐ์ผ.
[๊ฒฐ๋ก ]
์ฌ์ค ๋ด๊ฐ ๊ฐ๋จํ๊ฒ ์ฌ์ฉํ๋ ค๊ณ ๋ง๋ ๊ฑฐ๋ค ๋ณด๋ ํ๊ณ๋ ์กด์ฌํด.
1. ๊ฐ๋จํ ๋์๊ณผ ๋ฉ๋ชจ๋ฆฌ ํ ์คํธ๋ง ํด๋ดค๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ํ์ด ์์.
2. int, float, string ๊ฐ์ Primitive Type๋ค ์์ฃผ๋ก ์ฌ์ฉํ ๋ชฉ์ ์ด๊ธฐ ๋๋ฌธ์ ๋ง์ฝ Data Type์ด Class๋ Struct ์ธ ๊ฒฝ์ฐ๋ ๊ณ ๋ คํด๋ด์ผ ํจ.
3. ์๋ก ์์ ๋งํ๋ค์ํผ Github๋ ์คํ์์ค๋ก ๋ ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ง์ด ์กด์ฌํ๋๊น ๋ ๋ง์ ๊ธฐ๋ฅ์ด๋ ์์ ์ฑ์ ์ํ๋ฉด ์ฐพ์๋ณด๋ ๊ฒ๋ ๋์์ง ์์.
๋๋ ์ธ๋ถ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํด์ ์๋ ์ฝ๋๋ ์ข ๋ง์ด ๋ฌ๋๊ธฐ ๋๋ฌธ์ ์ต๋ํ ์์กด์ฑ์์ด ๋ฒ์ฉ์ฑ์๊ฒ ์์ ํ๋ค๊ณ ์๊ฐ์ด ๋ง์ด ๊ฑธ๋ ธ์ด.
๊ทธ๋ฆฌ๊ณ ๊ธ ์ฐ๋ฉด์ ๊ดํ ๊ธ์ผ๋? ์๊ฐ๋ ๋ค๊ณ ... ์๊น ์ฐ๋ค๊ฐ ํ๋ฒ ๋ ๋ ค๋จน์ด์๋ฆฌ ใ .ใ ์ด์ฐจํผ ๋ ์ข์ lib ๋ค๋ ๋ง์๋ฐ... ํ๋ฉด์.
์ง๋ฌธ์์ผ๋ฉด ๋๊ธ์ฃผ๊ณ , ์ด์จ๋ ๋์์ด ๋์๊ธธ ๋ฐ๋ผ!
๋!
source:
rx์จ๋ผ ๊ฒ์ด์ผ...
๋ง์ด RX๋ฅ๋ ์ข์ ์ ํ์ด์ง. ํ์ํด์ง๋ฉด ์ธ์ง๋ ๋ชจ๋ฅด๊ฒ ๋ค.
์ค ์ด๊ฑฐ ๊ถ๊ธํ๋๋ฐ ใฑใ ใฑใ - dc App