Memory

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the last demo, you implemented a basic phonebook with contact names and phone numbers. You worked with classes, structs, and initializers, and learned the difference between value types and reference types. You also learned new keywords: private and dump.

Understanding how your code reserves memory from the device helps you write more efficient code and allows you to better use the device’s resources. Having an idea how much memory your variables take and when they free it is very important for writing efficient code and can make your apps work faster.

This isn’t a topic you’ll master in a day or two, but it’s an important one to know and keep learning about.

You already know that when you create an instance of a variable, some space on the device memory is allocated for it. But the other part is when this space is released from memory!

The first point to cover is what a scope means. Code that is between curly braces { } has its own scope. Variables created inside a set of braces are accessible from within those braces and all other braces inside, but they’re not accessible outside it:

do {
  var intValue = 1
  do {
    intValue = 5
  }
}

intValue = 10 // This is an error.

A scope can be the code block in an if condition, a loop, a function, or any other code block.

For value types, once the scope the variable is created in ends, the memory that was reserved for that variable gets freed instantly. For reference types, it’s different.

Because you can have multiple variables referencing the same memory, and those variables can be of completely different scopes and possibly completely different parts of your app, a different mechanism is needed to identify when to free the memory safely. If the memory is released and a variable tries using it, the app crashes.

In older iOS code, that responsibility was left to the developer to release the memory themselves. But that meant human error was frequent. When a developer released it and then tried to access it, the app would crash. If they never released it, the app would use more and more memory. This is called Memory leak, and the app might eventually crash when there is no more memory left on the device.

One system that takes this responsibility from the developer and automates it is called Garbage Collection. It’s a complicated system that tracks all the variables in your app, who is using which variable, and all the allocated areas in memory. Every now and then, a cleanup process starts and frees up the memory that has no variables accessing it at all. It works great, but its only drawback is that the process to identify what memory to free takes a good amount of processing power, so there is always a tradeoff between CPU and memory usage.

Note: Garbage Collection never existed on iOS due to its heavy CPU demand. It existed on earlier versions of macOS.

Starting with iOS 4, Apple introduced a new system called Automatic Reference Count, or ARC. It doesn’t have the effect on processing that Garbage Collection does, but it requires some awareness by the developers so the system can do its job properly. So how does it work?

Internally, part of your application tracks how many references you created to a value. Creating a new reference to this value increments a count on it by one. Deleting that reference when its scope is closed decrements that count by one. Once the count reaches zero, the memory is freed immediately.

Here’s some code to walk through this:

class ExampleClass {}

do { // 1
  var variable1 = ExampleClass() // 2
  var variable2 = variable1 // 3
  do { // 4
    var variable3 = variable1 // 5
    variable2 = ExampleClass() // 6
  } // 7
} // 8

The first line creates a class type named ExampleClass.

Line by line, this code:

  1. Creates a new scope.
  2. Creates a variable — variable1 — with an instance from ExampleClass. The reference count for this value is one.
  3. Creates a variable — variable2 — to reference the value from variable1. The reference count is incremented to two.
  4. Creates a new scope.
  5. Creates a variable — variable3 — to reference the value from variable1. The reference count is incremented to three.
  6. Replaces the variable2 reference to a new value. That replacement reduces the reference count of the old value to two and sets the count on the new value to one.
  7. The end of the inner scope removes the reference of variable3, reducing the its value’s reference count to one. variable2 doesn’t get removed because it’s created in the outer scope.
  8. The end of the outer scope releases variable1 and variable2. Both had their values with reference count of one and are reduced to zero. Both values are removed from memory.

This is just the surface of how ARC works. There are more details in Swift memory management that would be too advanced to cover now, but this is a good start.

In the next demo, you’ll learn about the deinit function in class types and write some code to demonstrate object life cycle. You’ll also learn about the amount of memory value types allocate, including the numeric data types you used, and new value types you create in your code.

See forum comments
Download course materials from Github
Previous: Class & Struct Demo Next: Memory Demo