DOD — Unity Memory Best Practices — Part 7

Nitzan Wilnai
3 min readJun 6, 2024

--

Passing by Value vs by Ref

There is one more important thing to know about Structs and Classes. When we pass a struct object to a function, it is passed by value. When we pass a class object to a function, it is passed by reference.

Passing by Value — This means the actual value of the data is copied to the stack. On one hand, this is good because the data is on the stack. On the other hand, we had to copy the data, which takes time.

Let’s say we have the following function:

public int FunctionFoo(int param1, int param2)
{
int result = param1 + param2;
return result;
}

And we call it like this:

int var1 = 5;
int var2 = 6;
int var3 = FunctionFoo(var1, var2);

This is how our stack will look like before, during and after calling FunctionFoo()

---------------------------------------------------------------------
| Stack Memory |
---------------------------------------------------------------------
| Stack before calling | Stack during | Stack after exiting |
| FunctionFoo() | FunctionFoo() | FunctionFoo() |
---------------------------------------------------------------------
| var1 | var1 | var1 |
---------------------------------------------------------------------
| var2 | var2 | var2 |
---------------------------------------------------------------------
| | param1 | var3 |
---------------------------------------------------------------------
| | param2 | |
---------------------------------------------------------------------
| | result | |
---------------------------------------------------------------------

As we can see, the values of var1 and var2 were copied into param1 and param2.

If we were to change a parameter passed into FunctionFoo(), it would not change the original variable:

public void FunctionFoo(int param)
{
param = 10;
}

int var = 5;
FunctionFoo(var); // a copy of var got changed to 10
Debug.Log(var); // var is still 5

Passing structs to functions also copies the struct:

public struct MyStruct
{
public int Bar;
}

public void PassByValue(MyStruct myStruct)
{
myStruct.Bar = 5;
}

MyStruct myStruct;
myStruct.Bar = 10;
PassByValue(myStruct);
Debug.Log(myStruct.Bar); // myStruct.Bar is still 10

As we can see, myStruct.Bar is still 10 because the function PassByValue changed a copy of myStruct.

If we want a function to modify a struct, we need to pass it by reference.

Passing by Reference — This means we are passing the address of the data, not the actual data. No copy is made and the data remains where it was allocated. If the address is from the heap, local variables in the function will not be on the same cache line.

For example:

public void FunctionFoo(ref int param) // ref keyword added
{
param = 5;
}
int bar = 10;
FunctionFoo(ref bar); // a reference to var is passed
Debug.Log(bar); // bar is now 5

Here bar changes to 5 because a reference to bar is passed. In FunctionFoo we set param to the address of bar, so now param and bar are both pointing to the same data. When we change param, we are also changing bar.

Here is an example using Structs:

public struct MyStruct
{
public int Bar;
}

public void PassByReference(ref MyStruct myStruct)
{
myStruct.Bar = 10;
}

MyStruct myStruct;
myStruct.Bar = 5;

PassByValue(ref myStruct); // pass a reference to myStruct
Debug.Log(myStruct.Bar); // myStruct.Bar is now 10

Like in the integer example, myStruct.Bar was set to 10 because we passed the address of myStruct to the function PassByReference().

Classes are automatically passed by reference:

class MyClass
{
public int Bar;
}

public void PassByReference(MyClass myClass) // no ref needed
{
myClass.a = 10;
}

MyClass myClass;
myClass.Bar = 5;
PassByValue(myClass); // no ref needed
Debug.Log(myClass.Bar); // myClass.Bar is now 10

myClass.Bar changed to 10, even though we passed it without the ref keyword, because classes are automatically passed by reference.

Part 8 — https://medium.com/@nitzanwilnai/dod-unity-memory-best-practices-part-8-43a7bb5204b7

--

--