Null Reference Exception

return-to-sender

Unity Answers, Stack Overflow and the internet in general (related to programming) is drowned under null reference exception issues. Everyone faces it one day (sooner than later) and tries to fix it maybe without knowing accurately what it means.

(Read the following with Asian accent or Yoda voice) “If you know the enemy, the result of a hundred battles you need not fear”.

So we should go on to understand a little better why we encounter them more than we would like.

Using reference variables

The first step is to understand what we are using.
C# has two types of variables, value type and reference type. The value type contains the value, so an integer contains the actual value of 10,20,30 or else while a reference contains an address 0x0000AFBC, 0x8A5B6A3A or other hexadecimal values.

In C/C++, we had pointers, those are variables that can hold the address of another variable as their value. In C#, the idea is similar but it is called reference and it looks much simpler (no more -> , or ** getting the user lost). A reference variable can hold the address of an object that corresponds to the type it is declared.
So consider the following:

Animal animal = null;
animal = new Animal()

We declare an animal reference variable of type Animal. A new Animal object is created and its address is assigned to the reference. Only an object of type Animal can be passed. The Animal object may be at 0xAAAABBBB while the reference is stored somewhere else. But our animal reference variable has value of 0xAAAABBBB and when using the content of it, the compiler is smart enough to know the nature of the variable and uses it as a redirection to the actual object.

animal(0xAAAABBBB)
            |
            |-----> 0xAAAABBBB (Animal instance)                                         

All references are 4 bytes (in 32bit) since the address is four bytes long. his system allows the programmer to freely assign addresses to a reference as long as the type matches or if the type is a sub type of the reference. For instance, if Dog inherits from Animal, then a Dog object can be stored in an Animal reference and not the other way around since an Animal is not a Dog (ok you can cast).

the static case

There cannot be any NRE on static members for the simple reason that they do not depend on an object but on a class. The class is always there in memory ready to be used for creation of an object or to access static members. Since static are always in memory, they cannot rely on instance of an object and therefore are not allowed to access an instance member of a class. If static belongs to the class, it means a static member exists for that class and therefore cannot be duplicated.

To conclude with static, it cannot create NRE, it is faster than instance since the compiler does not to perform a check for nullity, it is always there and there can only be one.

Null Reference exception

The first line of our example below sets the animal reference to null. This means the content of the reference is 0x00000000. When the compiler hits that value, it knows that this is leading nowhere. We can actually check for nullity as the null value may be what we are expecting:

Animal animal = null;
if(animal == null)
{
    animal = new Animal();
}

this is fine and won’t crash since we are checking the value of the animal reference and if that value is null then our if statement is true.

You may get slightly confused about me telling about value of a reference type variable while I previously said there were value and reference types. Well, sorry to be a bummer but actually, I lied somehow…EVERYTHING IS A VALUE TYPE (think of why there areref and out keyword). Well, your reference type variable is actually a value type containing a reference value. Nonetheless, it is commonly considered two different types since they act differently.

Now let’s try more:

Animal animal = null;
animal.name = "MyPet";
animal = new Animal();
animal.age = 10;

Our first line declares the reference and sets it to null. So we have no object to be pointing at or referring to. But we try to access a member called name that we expect to be on an Animal object. When the compiler grabs the animal reference and tries to jump to the object it is meant to refer to, it finds a null value. Since there is nowhere to go, it throws the Null Reference Exception we all dread. This is the actual part that makes instance slower than static, the compiler performs a quick check prior to the jump.

The second part would succeed since the Animal object is created and then the age is reached. That means, on line 3 animal receives an address value that the ctor is giving, and line 4 means: get the animal variable content, the value is an address go there where an Animal object is stored, move down the pointer to the age member and assign the given value at that location.

What is the point of null if it is so bad? Well, it allows to know if something failed.

Rigidbody rig = GetComponent<Rigidbody>();

If the object contains a Rigidbody, the method wil find its memory address and assign it to the rig variable. It then holds the value of the address of the Rigidbody object.

Think a friend wants you to find a shop address online, you are looking for a shop address on the internet and once you find it, you write it on a paper. Then you give the paper to your friend, he can use the paper to go there, or give it to someone else, use part of the shop or even destroy it.
What if you did not find the address online, why should you write anything on the paper? If nothing is on the paper, your friend should not get lost in the city looking for the shop. GetComponent works the same, if no component is found, null is given. Then it is up to you to check if the reference variable is null or not.

So if you are not sure that an object exists, avoid direct calls like this one:

GetComponent<AnyComponent>().aMember = 10;

This can get even more complex if you use multiple level of references:

myObjectRef.GetComponent<AnyComponent>().someReference.aMember= otherObj.GetComponent<OtherType>().GetValue();

Problem is that Unity will throw the NRE, but it won’t tell exactly when and where it happened.
You can look at the stack trace to see when it breaks though. This is often pretty scary.

Actually, Objective-C and the new implementation of C# (not available on Mono yet) is using a new version that checks for nullity internally, it is called Null Propagation.

if (obj?.aMember == value)

See the ? before the dot? Some nconsider this dangerous as it makes debugging harder. Some may find it useful as it save a few lines of null check.

Missing Reference Exception

But what if the reference variable contains a value of an object that is not there anymore? Consider you have a reference to a GameObject in the manager script, then the GameObject is destroyed:

// Script A
GameObject obj = GameObject.Find("Player");
// in script B
if(player.Health == 0){
   Destroy(player.gameObject);
   player = (GameObject)Instantiate(playerPrefab);
}

So you have a reference in script A and in script B with both address 0x0000AAAA. When the player reaches the end of its life, you destroy it and create a brand new one with address 0x0000BBBB. Script B is aware of the new object and the address of it is passed to the player reference variable. But in script A, nothing was done and obj is still containing 0x0000AAAA. As a result, if you used obj.name = “NewName”; , the compiler would use the address it contained and jump there to find a dead object or something totally irrelevant. The compiler then throws a Missing Reference Exception as it used to have something there but it is no more the right one.

Another similar case happens when using persistent object over scenes. Your manager is marked as DontDestroyOnLoad and contains a reference to a non persistent object. That reference will persist over the loading of the scene but the target object has been destroyed. Your reference has a value for an address of a non-existing object.

Be careful with Destroy as it delays the object destruction to the end of the frame. So you could be able to continue using that object within the frame before it becomes broken. If you need immediate removal use DestroyImmediate.

And delegate?

First we need to clearly define what is a method.

public class Dog
{
    public void Bark(){}
}

The Dog class contains one public Bark instance method. So for each dog we create, it gets its own method. Wruung. Nope.

Actually, there will be only one instance of the Bark method that every instance of Dog will use. This allow to save memory space. The only difference is hidden in the call. The compiler allows itself to make a special addition, so when you do:

dog.Bark();

the compiler turns that into:

Dog.Bark(this Dog obj);

What that means? The method would be static and a reference to the caller is passed as parameter? Actually, this looks exactly like an extension method. Same signature, same principle and same usage.

To get an image of this, consider you are at the camping and want to wash dishes but there is only one sink. Every one has a UseSinkToWashDishes method, so you can do

camper.UseSinkToWasDishes();
// turned to Camper.UseSinkToWashDishes(this Camper obj)

So when one wants to use the sink, you get there with your own dishes and use the soap left by the sink (The soap is static since there is one for everyone and does not belong to any particular camper). The dishes are instance members and they are carried to the sink using the this parameter. Notice that this keyword is only available in instance methods? Sure it is local since it is a parameter.

The delegate instance is an object that among other information contains a reference to the method address and the caller address. This allows to reconstruct the method call when invoking the delegate. I will provide a basic implementation of the method as well

public void Bark(){
    float distance = Vector3.Distance(Vector3.zero, Vector3.one);
    int basicVolume = 10;
    int total = basicVolume + this.voiceVolume;
    Debug.Log("Barking at " + total + "db." );
}

Action action = dog.Bark(); 
action(); // ok
dog = null;
action(); // Not ok

In the second call, looking at the stack tracer, you will notice something special. Since the dog object is nullified you would expect the NRE to happen on the dog object but it won’t. I added a useless Distance method just so that you can see it happening on the stack trace without any harm, even the basicVolume assignment goes fine. It is the call to the this.voiceVolume that will break. The this pointer held in the delegate is now null and it was passed as null to the Bark method parameter. The reference to the method is still valid since the method does not belong to the dog but to Dog. Just like the sink is still at the camp but the camper is gone.

This is a case where understanding the reason for the NRE may sound illogical for someone who is not aware of the internal functioning. The same principle applies to events since they are basically delegates with extra features.

And this ends here.

1 Comment

Leave a comment