Objects in Swift
The core aspect of object-oriented programming (OOP) is of course the concept of objects. C++ began this paradigm in programming, while Java, C#, Apple's Objective-C, and other languages were all essentially built from this foundation.
Swift is an OOP language with the same dynamic object model as Objective-C, but presented in a cleaner, type-safe, and compact way.
You can think of an object exactly as it sounds, an abstract thing or container. An object can be something as simple as a string, or something as complex as the player object in the latest video game. Technically speaking, an object in a program is a reference to a set of various data in an allocated chunk of memory, but it's sufficient to just understand that an object can be a variable or a reference to an instance of a class, Struct, or block of code.
An object can have various data fields/aspects associated with it, such as properties, functions, parent objects, child objects, and protocols. In languages such as C for example, an integer variable is usually represented as just raw data, but the integer type in Swift is actually an object. Thus, we can access extra information and perform functions on Int
objects in our code. We previously saw this with the Int.max
variable, which returns the highest number that can be represented by the Int
class. Again, depending on the machine you are working on, this could be the same value as Int32.max
or Int64.max
.
var highestIntNumber : Int = Int.max
Access to functions and properties of an object uses dot notation, as we saw with the previous example. Int.max
and Int.min
are actually special properties known as class variables, which represent all instances of an Int
type object.
Let's look at how Swift deals with obtaining properties and functions of an instance of an object using a made-up Player
type object.
let currentPlayer = Player(name:"Fumi") //(a) let playerName = currentPlayer.getName() //(b) var playerHealth = currentPlayer.health //(c) currentPlayer.attackEnemy() //(d)
We'll get back to the second half of line (a)
, but just understand that it creates an instance of an object of the type Player
named currentPlayer
. Line (c)
creates a variable named playerHealth
that's set by the health
property of currentPlayer
; here with the dot notation. Lines (b)
and (d)
use the dot notation to call the functions getName()
and attackEnemy()
. The getName()
function in this case is a function that returns a string that's assigned to the constant, playerName
. Line (c)
creates a variable named playerHealth
that is created by referencing the health property of currentPlayer
, also using dot notation. Line (d)
is a direct call to the Player
class' attackEnemy()
function, which you can imagine for now just performs what would make currentPlayer
do her attack. This function doesn't return a value and thus is what's known as a void
type function.
As for line (a)
, one might note that it doesn't use the dot notation. This is how Swift does what's known as a class initializer; designated by the parenthesis ()
after the class name and with the parameter called name
: that sends a string, Fumi
, to the object's class initializer.
We will be diving deeper in to the use of objects momentarily as we move on to functions and classes.
Type safety and type inference
Objects and, as we'll see, functions on these objects in Swift are type-safe. What this means is that if we perform a function on a string object when the code was expecting an integer, then the compiler will warn us early on in the process. In the vein of game design, if we were to have the player perform an action only an enemy supposed to do, then Swift will know through its inherently type-safe nature.
Swift's type inference is something we've mentioned before. Unlike other languages where you have to declare the object's type every time it's initialized, Swift will infer what type you mean. For example, we have the following:
var playerHealth = 100 //Swift automatically infers that playerHealth is an Int object