같은 변수에 C# 참조와 Unity 객체 수명이 함께 있습니다
GameObject와 MonoBehaviour 같은 UnityEngine.Object는 C# managed 객체와 Unity native 객체가 연결돼 있습니다. Destroy가 처리되면 native 객체는 사라져도 C# wrapper가 당장 수거되지 않을 수 있습니다. 그래서 변수에 C# 참조가 남아 있는데 Unity 쪽 객체는 이미 파괴된 상태가 생깁니다.
Unity 6.0과 6.6 매뉴얼 모두 UnityEngine.Object의 == null이 이 상태를 null처럼 판단한다고 설명합니다. 따라서 ReferenceEquals나 C#의 null 조건 연산자 결과가 == null과 다를 수 있습니다.
두 검사를 한 번 실행해 상태를 비교합니다
아래 스크립트를 새 GameObject에 붙이고 target에 씬의 다른 GameObject를 지정한 뒤 Play를 누르세요. Destroy는 현재 Update loop가 끝난 뒤 실제 처리되므로 코루틴은 한 프레임을 기다렸다가 검사합니다.
할당한 대상이 파괴된 뒤 saved == null은 true가 됩니다. 로컬 변수 saved가 C# wrapper를 붙잡고 있는 동안 ReferenceEquals(saved, null)은 false일 수 있습니다. 이 차이가 Unity 객체가 실제로 파괴됐다는 단서입니다. Inspector에 대상을 할당하지 않은 경우도 saved == null이 true이므로, 예제는 반드시 target을 지정해 비교하세요.
using System.Collections;
using UnityEngine;
public sealed class DestroyedReferenceProbe : MonoBehaviour
{
[SerializeField] private GameObject target;
private IEnumerator Start()
{
GameObject saved = target;
Destroy(target);
yield return null;
Debug.Log($"Unity == null: {saved == null}");
Debug.Log($"C# ReferenceEquals null: {ReferenceEquals(saved, null)}");
if (saved == null)
{
Debug.Log("Unity object is gone; do not access its members.");
yield break;
}
Debug.Log(saved.name);
}
}`?.`와 `??`로 Unity null 검사를 대신하지 않습니다
Unity 공식 매뉴얼은 UnityEngine.Object 파생 타입에 C# ?.와 ??를 쓰면 ==와 같은 결과가 나오지 않는다고 설명합니다. 이 연산자들은 managed 참조의 null 여부를 확인하고 Unity의 native counterpart 상태를 확인하지 않습니다. Unity 객체를 사용하기 전에는 Unity가 재정의한 obj == null 검사를 사용하세요.
// UnityEngine.Object 파생 타입
if (target == null)
return;
target.transform.position = Vector3.zero;예외 이름은 원인 판정의 첫 단서일 뿐입니다
NullReferenceException은 실제 C# null 참조에서 멤버를 사용한 경우를 먼저 의심하게 합니다. Unity 객체가 이미 파괴된 wrapper라면 Unity API 멤버 접근에서 MissingReferenceException 메시지가 나올 수 있습니다. 다만 예외 문자열만으로 모든 Editor·Player 상황을 분류하지 마세요. stack trace의 발생 행, 해당 변수의 실제 할당, obj == null, ReferenceEquals(obj, null) 결과를 함께 확인하세요. Inspector 미할당 상태나 Editor 전용 fake-null 진단도 obj == null처럼 보일 수 있습니다.
예외를 잡기 전에 수명을 고칩니다
try/catch (NullReferenceException)을 덧대는 것으로 파괴된 객체의 수명이 연장되지는 않습니다. Unity null 검사 뒤 안전하게 빠져나오도록 참조 수명을 정리하는 편이 우선입니다.
- stack trace가 가리키는 행에서 사용한 Unity 객체와 그 참조를 마지막으로 설정한 코드를 찾습니다.
- 객체를 찾지 못해 실제 C# null이 들어온 경로인지,
Destroy또는 씬 전환 뒤 객체가 사라진 경로인지 확인합니다. - 사용 직전에
obj == null을 검사하고, 사라졌으면 현재 작업을 종료하거나 참조를 다시 찾습니다. - 오래 보관하는 캐시·static 필드·이벤트 구독을 쓴다면 씬 종료나 객체 해제 시 함께 비웁니다.
- Editor에서 보인 결과를 Player에서 동일하게 나온다고 가정하지 말고, Player 오류는 그 빌드의 stack trace와 조건으로 따로 확인합니다.
공식 문서 대조
검증일
Unity 6.0과 6.6의 UnityEngine.Object 및 API 문서를 대조했다. 코드 예제는 문서 기반이며 Unity Editor에서 실행하지 않았다.