Understanding value types

You just saw how passing around a reference type can yield results you might not expect. The idea behind a value type is that this doesn't happen because instead of passing around references to addresses in memory, you're passing around actual values. Doing this will often lead to safer and more predictable code. As if this isn't enough benefit on its own, value types are also cheaper to instantiate than reference types. More on that later. We'll focus on the most visible differences first.

Differences between values and references in usage

Let's take a look at the Pet example again; this time we'll use a struct instead of a class:

struct Pet { 
    var name: String 
     
    init(name: String) { 
        self.name = name 
    } 
} 
 
func printName(forPet pet: Pet) { 
    print(pet.name) 
} 
 
let cat = Pet(name: "Bubbles") 
 
printName(forPet: cat) // Bubbles 

The initial example for our value type exploration looks nearly identical to the reference-type version. The only notable difference is that we're now using a struct instead of a class in the definition of Pet. If we attempt to make the same change to the name of our pet instance as we did before, it won't work. Try to alter the printName function as follows:

func printName(forPet pet: Pet) { 
    print(pet.name) 
    pet.name = "Jeff" 
} 

You will get an error saying that pet is a let constant. We didn't declare pet anywhere except for the function parameter, and we sure didn't declare it to be a let constant. The reason we're seeing this error is because any value type passed to a function is considered to be constant; it's immutable. If we want to mutate the pet name anyway, we will need to do the following:

func printName(forPet pet: Pet) { 
    print(pet.name) 
    var mutablePet = pet 
    mutablePet.name = "Jeff" 
} 

This will create a mutablePet variable, and then the name for this mutablePet is changed. If you print the name for the original pet after calling printName, what do you think will be printed? If you guessed that it will print Jeff, you guessed wrong because that's the reference-type behavior. The name of our pet is still Bubbles. This is the case because we never passed around the address in memory for our initial Pet instance. We only passed its value around. This means that every time we assign one variable to another, or if we pass a value type to a function, a copy is created instead. Consider the following example that we saw before when using a reference type:

let cat = Pet(name: "Bubbles") 
var dog = cat 
 
printName(forPet: cat) // Bubbles 
dog.name = "Benno" 
print(cat.name) // Bubbles 

You can see that no unexpected mutations were made. You might also notice that the dog variable was declared as a variable rather than a constant. Value types are a lot stricter when it comes to mutation. If a value type is declared as a constant, none of its members can be mutated.

Memory differences between values and references

The predictability of value types is great; it can really save you some headaches because values don't change unless you explicitly define them to be changed. Also, they will never be changed at times you don't expect because you don't pass them around by reference.

Another big plus that value types have is that they are a lot faster to create in memory. This is because value types are typically created on the stack, whereas reference types are created on the heap. Let's take a brief look at what this means and why it matters because it's actually good to be aware that the type of object you pick can influence your application's performance.

Heap allocation

Objects that get allocated on the heap can change their size at any time. A good example of an object that is allocated on the heap is a mutable array. This kind of array can be modified so elements can be added and removed. This means that your app can't allocate a fixed size of memory for this array, and it has to grow and shrink the allocated memory based on the required size.

Classes are also allocated like this; the compiler assumes that a class might have to resize its required memory at any time. One of the reasons for this is that classes aren't as strict about mutability as structs are. In our experiments earlier, you saw that you could change the name property of a class instance that was declared with let. Because this is possible, the size in memory for our instance could change whenever we assign a new name.

As the memory allocation for reference types is very dynamic, it's also a lot slower to create instances of reference types. The memory overhead is a lot bigger than the overhead caused by value types that are allocated on the stack. Let's take a look at those now.

Stack allocation

As mentioned, value types are allocated on the stack. Stack allocation means that an object has a fixed size in memory that does not change. Sometimes, this fixed size is known at compile time. An example of this is Int. The compiler knows how much memory an integer value can use, so it knows how much to allocate ahead of time. This leads to a much faster instantiation process because it's a lot easier to optimize for this scenario.

Value types are also allocated on the stack because once they are allocated they typically don't change size anymore. There are some cases where a value type will still need to use the heap; for instance, if a value type contains a constant that is a reference type. You shouldn't worry about these cases too much though. In general, value types are simply faster due to the way they are allocated.

As a side note, it's worth mentioning that stack allocated objects don't always take up memory. Often, a value type can be kept in the CPU instead of memory, which makes value types even more efficient.

Now that you know more about the way value types and reference types impact your app's memory usage and speed, let's take a look at how structs can improve your code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.138.202