Reference types

The types you have seen so far in this book were mostly, if not all, reference types. Two types of objects are classified as reference types:

  • Classes
  • Closures

You have seen both of these object types in this book already. For instance, all UIViewController subclasses you have created are reference types. All closures that you used as callbacks or to perform animations in are also reference types.

So what does it mean if something is a reference type, and why should it matter to you? Well, reference types come with behavior that can be both convenient and very frustrating depending on what you are trying to achieve in your code.

One feature that is unique to reference types and classes is the ability to subclass. The only type that can inherit functionality from another object is a class. This will be covered in more depth when you learn about the differences between types, but it's good to be aware of this information already. Let's examine reference types up close by writing some code in a Playground.

Create a new Playground project in Xcode and give it a name. Then add the following code to it:

class Pet {
  var name: String

  init(name: String) {
    self.name = name
  }
}

func printName(for pet: Pet) {
  print(pet.name)
}

let cat = Pet(name: "Misty")
printName(for: cat)

It's likely that you're not too excited about this little snippet of code. All it does is define a new Pet class, make an instance of it, and then it passes that instance into printName(for:). However, this code is extremely well-suited to illustrating what a reference type is.

When you call printName(for: cat), you pass a reference to your cat instance to printName(for:). This means that it is possible for anybody who gets ahold of this reference to make changes to the object that is referenced. If this sounds confusing, that's okay.

Add the following code to the Playground you have created and then run it:

func printName2(for pet: Pet) {
  print(pet.name)
  pet.name = "Jeff"
}

let dog = Pet(name: "Bingo")
printName2(for: dog) // Bingo
print(dog.name)

What do you notice in the console after running this? If you noticed that the dog's name changes from Bingo to Jeff, you have just observed what it means to pass a reference to something around. Since printName2(for:) received a reference to your Pet instance, it was able to change its name. If you have programmed in other languages, this might be obvious to you. If not, this might be very surprising.

One more thing you should note is that dog was declared as a constant. Regardless, you were allowed to change the name of your instance from Bingo to Jeff. If you think this is obvious, add the following code to your Playground and run it:

import UIKit
let point = CGPoint(x: 10, y: 10)
point.x = 10

This code is very similar to what you did with the Pet instance. You make a constant instance of a thing, and then you change one of its properties. This time, however, when you try to run your Playground, you should see an error along the lines of:

 Cannot assign to property:'point' is a 'let' constant.

Even though the code you implemented so far is pretty small, it does a great job of demonstrating reference types. You have currently seen two properties of a reference type in action:

  • Anybody that receives an instance of a reference type can mutate it
  • You can change properties on a reference type, even if the property that holds onto the reference type is declared as a constant

These two characteristics are typical of reference types. The reason reference types work like this is that a variable or constant that is assigned a reference type does not contain or own the object. The constant or variable only points to an address in memory where the instance is stored.

Any time you create an instance of a reference type, it is written to RAM where it will exist at a particular address. RAM is a special type of memory that is used by computers, such as an iPhone, to temporarily store data in that is used by a certain program. When you assign an instance of a reference type to a property, the property will have a pointer to the memory address for this instance. Have another look at the following line of code:

let dog = Pet(name: "Bingo")

The dog constant now points to a particular address in memory where the Pet instance is stored. You are allowed to change properties on the Pet instance as long as the underlying memory address isn't changed. In fact, you could theoretically put something entirely different at that memory address, and let dog won't care because it still points to the same address.

For this same reason, it is possible for printName2(for:) to change a pet's name. Instead of passing it an instance of Pet, you pass it the memory address at which the instance is expected to exist. It's okay for printName2(for:) to make changes to the Pet instance because it doesn't change the underlying address in memory.

If you tried to assign a new instance to dog by typing the following, you would get an error:

dog = Pet(name: "Nala")

The reason this would error is that you can't change the memory address dog points to since it's a constant. The following image should clarify this a little bit more by visualizing what it means to pass around a reference type:

Now that you know what a reference type is and how it works, you might have already concluded that the CGPoint you saw in the preceding example must be a value type. Let's see what value types are all about next.

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

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