알림 규칙

구독자가 알림을 받는 도중에 구독하거나, 구독을 해제하거나, 값을 되쓸 때 무슨 일이 일어나는가.

업데이트 2026-09-08

대개 구독자는 값을 읽고 뭔가를 갱신할 뿐입니다. 이 문서는 그 나머지, 즉 구독자가 자기 콜백 안에서 구독자 목록이나 값 자체를 바꿀 때의 이야기입니다.

구독자는 자기 옵저버블에 되써서는 안 됩니다

이건 컴파일되지 않습니다.

score.Subscribe(Clamp);

private void Clamp(int value)
{
    if (value > 10)
        score.Value = 10;      // UDN0012
}

Udonite가 거부합니다. 구독자가 자기 옵저버블로 다시 닿는 것은 델리게이트를 통한 순환이고, Udon은 간접 호출을 위해 프레임을 밀어 둘 수 없기 때문에, 그렇게 만들어진 프로그램은 제대로 돌아오지 못합니다.

거부되는 쪽이 좋은 결말입니다. 여기서 걸리는 형태에는 되돌아가는 한 걸음이 메서드가 아니라 프로퍼티 세터인, 눈에 덜 띄는 경우도 들어갑니다. 그걸 그대로 돌리는 월드는 빨리 죽지도 않습니다. VRChat이 Program execution time exceeded max VM time of 10,0 seconds로 behaviour를 죽일 때까지 얼어붙어 있고, 남는 것은 10초 동안 멈춘 월드와 쓸모없는 메시지 하나뿐입니다.

대입하기 전에 먼저 잘라내세요.

score.Value = raw > 10 ? 10 : raw;

아니면 다른 옵저버블에 쓰세요. 그건 지극히 평범한 일입니다.

알림 도중의 구독 해제는 다음 알림부터 적용됩니다

구독자는 콜백 안에서 자기 자신이든 다른 누구든 구독을 해제할 수 있습니다. 진행 중인 알림은 시작할 때의 구독자 그대로 끝까지 돌고, 해제는 그다음 알림부터 적용됩니다.

private void Once(int value)
{
    score.Unsubscribe(Once);      // 괜찮습니다. 나머지는 그대로 호출되고,
}                                 // Once는 다음부터 호출되지 않습니다

이건 C# 이벤트와 같습니다. 구독자를 빼면 순회 중인 목록을 고치는 대신 새 목록이 만들어집니다. 콜백 안에서 구독을 해제해도 안전한 이유가 바로 그것입니다.

그래서 정리 중인 behaviour가 딱 한 번 더 호출될 수 있습니다. 이미 돌고 있는 회차에서요. 객체가 사라진 뒤에 돌면 안 되는 핸들러는 구독 해제에 기대지 말고 핸들러 자신이 막으세요.

private void OnScoreChanged(int value)
{
    if (closing)
        return;

    label.text = value.ToString();
}

알림 도중의 구독은 다음 알림을 기다립니다

알림이 진행 중일 때 추가된 핸들러는 그 알림에서는 호출되지 않습니다. 자기가 있던 시점의 무언가를 놓친 것도 아니고, 방금 합류한 회차 안에서 호출되는 편이 오히려 놀라운 일이니까요.

두 번 구독해도 두 번째는 아무것도 하지 않습니다

이중 구독이 되는 흔한 경로는 Start가 다시 도는 경우입니다. 변경마다 두 번 호출되는 건 언제나 버그이므로, 두 번째 구독은 받아들이지 않고 무시합니다.

모두에게 알리기, 한 명에게 알리기

Publish()는 뭔가 바뀌었든 아니든 모든 구독자에게 알립니다.

늦게 들어온 한 명만 최신 상태로 맞추고 싶다면 Subscribetrue를 넘기세요. Publish로도 되긴 하지만, 그러면 이유 없이 나머지 전부가 다시 그려지고, 횟수를 세는 구독자가 있다면 숫자가 틀어집니다.

score.Subscribe(OnScoreChanged, true);   // 이 핸들러만
score.Publish();                          // 전부

읽기는 언제나 조용합니다

Count, Contains, IndexOf, ToArray, ContainsKey, TryGetValue와 모든 게터는 아무에게도 알리지 않습니다. 알리는 건 변경뿐입니다.

SetSilentlyAddSilently는 알리지 않고 바꿉니다. 구독자가 반응할 이유가 없는 초기값, 예컨대 입장할 때 복원한 점수 같은 것을 심어 두기 위한 것입니다.

문제가 발생했습니다 새로 고침