We recently wrote about Swift structs. In this tutorial, we’ll look at classes. Classes and structures have a lot in common, although there are some important differences. Let’s take a look at what a class is and what you use them for.
- A class is initialised as an object.
- You need to create an initialiser for your class.
- Classes can also inherit from other classes which bring to your class the abilities and properties of the other class. You can also override the default behaviour of a method of the parent class.
- Unlike structs which are passed around by value, classes are passed by reference.
You can describe a class as being a blueprint which is used when creating objects. That blueprint defines the behaviours of the object as well as the properties of the object. If you think of a car as a class, you might have the following:
class Vehicle {
var wheels: Int
var colour: UIColor
var remainingFuel: Int?
var topSpeed: Int
init(wheels: Int, colour: UIColor, remainingFuel: Int?, topSpeed: Int) {
self.wheels = wheels
self.colour = colour
self.remainingFuel = remainingFuel
self.topSpeed = topSpeed
}
func accelerate() {
print("This is awesome!!!")
}
}
The class above is known as a base class. We have called it Vehicle. This class could be used as a blueprint for all types of vehicles. This class could be instantiated and used as-is. With it we would know how many wheels the vehicle has, the colour of it, the location of it, and how much fuel is in there. Notice how remainingFuel on line 4 has a ? after the type. This indicates that its option and may or may not contain a value.
On line 7 we have the initialiser. Because we have three non-optional properties, an initialiser is required. We set self.nameOfProperty to a value passed in, or alternatively, you can hard code a value in here if you like. If you wanted to insist that all cars are green, you could just remove that from the init method signature and set self.colour = UIColor.green, but in this instance, there is no need for us to do this.
We also have a function called accelerate which prints a message to the console.
Using some imagination, this class is technically usable. But we all know that vehicles have many more attributes. A vehicle could be a bike, a car, a rocket, a truck, and so on. This is where inheritance helps us because we can add to the common features found in vehicles.
Inheritance allows you to improve another class. Although the class you inherit from is the superclass which makes it sound like it does more, it’s actually the reverse. You inherit a class and add more abilities to it, thus, making it a little better.
Let’s create a new class. We’ll call it Rocket. Because a rocket is a vehicle, we will inherit this class. We declare it as follows:
class Rocket: Vehicle {
}
We call the class, Rocket. The : Vehicle means that this class inherits from Vehicle which also means that it automatically gains all of the properties and attributes of a Vehicle.
We can choose to add our own methods, and/or override the method(s) that already exist.
class Rocket: Vehicle {
override func accelerate() {
print("This is very fast!!!")
}
}
In this example we are overriding the accelerate method. We can do whatever we want with it. Most likely this method would be the instructions to the rocket on how to accelerate such as flip ignition switch and away you go, but in a car, accelerate would be something different.
class Rocket: Vehicle {
override func accelerate() {
print("This is very fast!!!")
}
func seperate() {
print("The rocket is seperating")
}
}
Rather than only overriding accelerate, we also want the rocket to have the ability to separate. Because Vehicle doesn’t know how to separate, we add this functionality to just the rocket by adding a new method called separate.
You can also add more properties such as a trajectory:
class Rocket: Vehicle {
var orbitTrajectory: Int
override func accelerate() {
print("This is very fast!!!")
}
func seperate() {
print("The rocket is seperating")
}
}
You will notice at this point that an error is now showing. orbitTrajectory needs initialising. This means we need to add an init method.
init(wheels: Int, colour: UIColor, remainingFuel: Int?, topSpeed: Int, orbitTrajectory: Float) {
self.orbitTrajectory = orbitTrajectory
super.init(wheels: wheels, colour: colour, remainingFuel: remainingFuel, topSpeed: topSpeed)
}
Add this method in just below var orbitTrajectory…
You may note that we don’t put “func” in front of an init method. This is because it isn’t required.
So we create an init method for rocket that accepts all of what its superclass needs which is wheels, colour, remainingFuel, topSpeed, and we add in orbitalTrajectory.
Just after that we call super.init and pass in the variables to super which is the parent (ie, Vehicle). This initialises Rocket with what it needs, and also satisfies the init method on Vehicle.
To test the code together you can open up a new Swift Playground and copy/paste the following:
import UIKit
class Vehicle {
var wheels: Int
var colour: UIColor
var remainingFuel: Int?
var topSpeed: Int
init(wheels: Int, colour: UIColor, remainingFuel: Int?, topSpeed: Int) {
self.wheels = wheels
self.colour = colour
self.remainingFuel = remainingFuel
self.topSpeed = topSpeed
}
func accelerate() {
print("This is awesome!!!")
}
}
class Rocket: Vehicle {
var orbitTrajectory: Float
init(wheels: Int, colour: UIColor, remainingFuel: Int?, topSpeed: Int, orbitTrajectory: Float) {
self.orbitTrajectory = orbitTrajectory
super.init(wheels: wheels, colour: colour, remainingFuel: remainingFuel, topSpeed: topSpeed)
}
override func accelerate() {
print("This is very fast!!!")
}
func seperate() {
print("The rocket is seperating")
}
}
let rocket = Rocket(wheels: 0,
colour: UIColor.white,
remainingFuel: 100000,
topSpeed: 17671,
orbitTrajectory: 7.289)
rocket.colour
rocket.accelerate()
rocket.seperate()
rocket.orbitTrajectory
Towards the bottom we declare that we want a rocket creating. We pass in all the required details for Rocket and those required by Vehicle, and we can then access rocket.colour, and all other properties and methods of that class.
Adding a new type of Vehicle
Lets say we now need a bike. Lets add that:
class Bike: Vehicle {
override func accelerate() {
print("This is a little slower than my rocket. To accelerate, I need to pedal faster and faster.")
}
}
To get all the functionality of a vehicle, but for a bike, we can create a new class that is a subclass of a vehicle. We can choose to override whatever we want, in this case the accelerate function, and add in another function to change the front gear, and also add some properties to keep track of what gear the front and rear sets are in.
class Bike: Vehicle {
var frontGear: Int
var rearGear: Int
init(wheels: Int, colour: UIColor, remainingFuel: Int?, topSpeed: Int, frontGear: Int, rearGear: Int) {
self.frontGear = frontGear
self.rearGear = rearGear
super.init(wheels: wheels, colour: colour, remainingFuel: remainingFuel, topSpeed: topSpeed)
}
override func accelerate() {
print("This is a little slower than my rocket. To accelerate, I need to pedal faster and faster.")
}
func changeFrontGear(gear: Int) {
print("Move derailler to position: \(gear)")
}
}
Just like Rocket, we init to add in the extras for this class, and then call super.init to initialise those properties in the superclass.
We can now create our bike as follows (includes Rocket code as well):
let rocket = Rocket(wheels: 0,
colour: UIColor.white,
remainingFuel: 100000,
topSpeed: 17671,
orbitTrajectory: 7.289)
let bike = Bike(wheels: 2, colour: UIColor.green, remainingFuel: nil, topSpeed: 50, frontGear: 1, rearGear: 2)
rocket.accelerate()
rocket.seperate()
bike.accelerate()
bike.changeFrontGear(gear: 2)
You can see that we instantiate a rocket object, and then a bike object. Both require the same and slightly different properties. Rocket can accelerate and separate, and the bike can also accelerate, but can also change the front gear.
It we tried calling bike.seperate() we would get an error because a bike cannot separate (well, if it did there would be a problem).
Copying a Class
When you copy a class it copies by reference, meaning that both the new and the original will share the same object. If you change gear on bike, but then have newBike which is a copy of bike, then both newBike and bike will move to gear 1, as seen below:
bike.changeFrontGear(gear: 2)
var newBike = bike
newBike.frontGear
bike.changeFrontGear(gear: 1)
newBike.frontGear
bike.frontGear
On line 1 we set the front gear to 2.
Next we copy bike to newBike.
We then access the frontGear (in Swift Playgrounds). You’ll see that the gear is 2.
We then change the gear on the original bike to 1.
We then look at what the frontGear is for both newBike and bike. Notice that we only changed “bike”, but both bikes are now in gear 1.
This is because classes are copied by reference. Rather than copying the whole Bike to a new variable, just the memory location of bike is copied meaning that if you change gear on either/or of the bikes, it will be exactly the same on the other. More details can be found on Swift.org.
This makes classes ideal for when the state needs to be the same. If you want to have two bikes then you either need to instantiate a new bike or create bike as a struct.
In the next tutorial we’ll look at some ways how to decide when to use a class or a struct.
Leave a Reply
You must be logged in to post a comment.