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