Hey, guys. 1
Welcome to another Swift Deep Dive. In this Deep Dive, 2
we're going to look at the difference and the similarities between structs and classes and when you should 3
choose to use each. 4
So previously, we really saw that we could create a class like so. And by simply changing that class keyword, 5
we can create a struct. 6
But, of course, structs are unable to inherit. 7
So they don't have the part that classes have where they're able to specify a SuperClass that they can 8
inherit capabilities from. 9
But there are a lot of similarities, too. 10
So in this class that we've created where we're not inheriting from any SuperClass, it almost looks like 11
a struct. And similar to struct, 12
we're essentially creating a blueprint to be able to group together attributes and behavior into one 13
component, 14
and later on, we can use that blueprint to create an object from it, and we can use it to create as many 15
as we want. 16
So we could spawn, for example, a whole bunch of skeletons simply by tapping into that Class blueprint 17
or a Struct blueprint. Now similar to struct, 18
we also have initializers in classes, so I could delete these default values and simply specify their 19
data type. 20
And these are both gonna be integers, and then I can initialize it. 21
Now, notice how if we have a struct, we get a free initializer. 22
So we don't actually have to write the init method. But if we had a class on the other hand, then we would 23
get an error saying that class enemy has no initializers, 24
so we have to provide the init ourselves, and I will do exactly that. So I'll provide an input code health 25
which has a date type of Int, and then I have another one called attackStrength which is also an Int. 26
And once those inputs get passed in, I'm going to set the self.health which, of course, refers 27
to this current classes health property, which is this one, and I'm going to set it equal to the health 28
that got passed in as the input and I'll do the same with my attackStrength property. And now when I 29
go and initialize my skeletons, for example, let's say create skeleton1, I have to provide those arguments, 30
right? 31
So if we go ahead and create an enemy and open a set of curly braces, you can see both the health and 32
attackStrength parameter expects a value, so that I can initialize the skeleton object from my class 33
and pass in the necessary values for my properties which don't currently have a value. 34
So let's say that the skeleton1 has a health of 100 attack strength of 10. 35
Now, here comes the curious thing. if I go into my enemy class and I create another function, so let's 36
call it takeDamage. 37
So let's say that our enemy got hit, well, then we can specify a damage amount. 38
So let's call that amount, 39
set it as an integer. 40
And then inside the function, we're going to update the health property to deduct the amount of damage 41
that it got dealt. 42
So now when we call the takeDamage method, our enemies health should be reduced by the amount that they 43
got damaged by. 44
And now if I want to create another skeleton, 45
well, if I wanted another skeleton, say, let skeleton2, for example, I want to be exactly the same as 46
skeleton1, 47
so why don't I just set it to equal skeleton1? 48
So looking at this code, it would appear that skeleton2 is a copy of skeleton1. 49
Let's delete the dragon code and investigate if this is indeed the case. 50
Let's see what happens when skeleton1 takes some damage. So skeleton1 took some damage, 51
So .takeDamage. And let's say it got damaged by 10. 52
Now here's a question: If I was to print and get hold of my skeleton2 object and I wanted to access 53
its health property, what do you think that should be? 54
Remember that I copied skeleton2 from skeleton1 at the point where skeleton1 was just initialized, 55
so it had full health, and they got to attackStrength of 10, and then skeleton1 took some damage, but 56
skeleton2 didn't. 57
Nothing happened to skeleton2, it was just sitting around waiting to attack. 58
So what do you think this line would print? 59
And I want you to have a think about it before you run this code because it's quite curious. All right, 60
so if I run the code, you can see that what gets printed is actually 90. 61
Why is it that skeleton2 which didn't take any damage at all, 62
why does it have a health of 90? 63
Well, this is because classes are passed by reference. 64
So here on this line, where I created a copy of skeleton1, I haven't actually created a real copy, 65
all I've done is I've created another reference to the same object. 66
So this is basically a second reference to the same skeleton object that I initialized here. Had I wanted 67
to create a whole new object, 68
I would have to, essentially, copy this code and initialize a new enemy. For if I did this, 69
you can see that skeleton2 is completely unscathed 70
when skeleton1 takes damage. This is kind of dangerous though, because just by looking at this code, 71
it doesn't look like as if that's what would happen. 72
And this is why classes tend to be considered as more complex and, therefore, more error-prone than struct. 73
Because in the case of a class, this copying behavior is quite unintuitive, 74
and we've now got two references to the same object. 75
And if we manipulate that object from skeleton1, say, take some damage of 10 and we manipulate the 76
object that we think is a separate object. 77
We take some damage. 78
Well, actually, what ends up happening at the end of the day is that both of these things are actually 79
referring to the same thing. 80
So if I go ahead and print my skeleton1.health and skeleton2.health here, you can see 81
that they're both going to be the same, and they actually in total--took a total of 30 damage, even though 82
I thought I was manipulating separate objects. iIf we had instead created a struct, instead of a class, 83
well, firstly, in a struct, we're reminded when we actually mutate our struct and that happens through 84
this mutating keyword. Because this function will update a property of the struct, 85
it alerts us to this fact already, and we won't be able to do this without making it mutating because, 86
remember, struct are immutable. 87
Now, when this function happens, the old struct gets destroyed and a new one with the updated value gets 88
created. 89
But when I make a copy of a struct, it really does make a whole new copy and they're completely separate. 90
So now if I tried to run this code, it gives me another reminder that this struct is gonna be changed. 91
It says that "Cannot use mutating member on immutable value: 'skeleton1' one is a 'let' constant. 92
So this is a constant and this is a constant. 93
So when we run that takeDamage as to destroy the old object and create a new one in its place, but because 94
we have created a constant here with the "let" keyword, it is not able to do that, 95
so we have to change this to "vars." But now if I try to run my code, the first thing you'll notice is that 96
I lose my capability of inheritance. 97
So my Dragon class has to be commented out because Enemy is now a struct and it can no longer be a SuperClass 98
or inherit from any class. 99
But if we're still looking at our main.swift here and I run my code, you can see that skeleton1's 100
health went down to 80 because it took 10 damage on line 5 and line 8. And skeleton2's health, 101
the one that was printed second, actually only took 10 damage, so it still has a health of 90. 102
So now you can see that our struct is being copied and skeleton2 is no longer a reference to the same 103
object that was created in skeleton1. 104
It's, in fact, a completely separate copy which means that when I do things to it, it doesn't affect my 105
skeleton1 on my previous copy. 106
And so this makes it much simpler to understand the side effects and what might happen when I manipulate 107
these objects. 108
One of the biggest differences between struct and classes is that struct are passed around by value. 109
So what that means is that if I had a photo and I wanted to give it to you, then I could make a copy of it 110
and give you that copy. 111
Now, classes, on the other hand, are passed by reference. 112
So if I wanted to give you access to my photo, I would tell you where my photo is located, so I can give 113
you the location of it on my computer. 114
I could say it's under Users/angelayu/desktop/photo.jpeg, and you'll find it there. 115
Now, the problem occurs when lots of people need access to the same resource. Now in the case of a struct, 116
if lots people have multiple copies of the same thing, 117
well, if they destroy their copy of it, 118
say, you decide to draw whiskers onto my face 119
in this lovely photo that I gave you, 120
well, you've only just destroyed your own copy. 121
Everybody else's copy is unaffected. 122
Now, on the other hand, if lots of people had access to the same resource based on a reference, 123
so they all knew where it was on my computer, 124
and one of them accidentally deletes it, 125
well, that's kind of it for all of us. 126
We've all lost that resource now. So that's why Apple advises that you always default to creating a struct. 127
So start by creating a struct whenever you need that blueprint to create new objects. 128
But if you find that you need inheritance or you need to work with old Objective-C code, then you can 129
simply turn your structure into a class. 130
So that wraps up our Deep Dive on structures and classes. 131
And to recap, here's a brief summary of some of the things that we talked about. 132
Structures are immutable, meaning that every change that you make to the structure involves destroying 133
the old copy and creating a new one. 134
In addition, we saw how structures are passed by value. So it's passed around by the actual value that's 135
held. Now, in comparison, 136
classes are passed by reference, meaning that we can have more than one variable that points to the same 137
object, 138
and we're simply passing around the reference to that same object. 139
In addition, classes have the ability to inherit from other classes. 140
This means that we can use inheritance to add additional functionality to SubClasses as we saw with 141
the components of Apple's UIKit. 142
So if you'd like to learn more about structures versus classes or read about each of those in more detail, 143
I recommend heading over to this page in the Swift Programming Language Guide and taking a look at it. 144
So, a link to this page,and you can get more of a background on structures and classes.