ARC Swift Tutorial

iOS Memory management made easy.

As a modern, high-level programming language, Swift handles much of the memory management of your apps and allocates or deallocates memory on your behalf. It does so using a neat technology called Automatic Reference Counting, or ARC.

ARC: Automatic Reference Counting
May the garbage collector be with you.

Functions of ARC

  1. ARC allocates a chunk of memory to store the information each and every time when a new class instance is created by init().
  2. Information about the instance type and its values are stored in memory.
  3. When the class instance is no longer needed it automatically frees the memory space by deinit() for further class instance storage and retrieval.
  4. ARC keeps in track of currently referring class instances properties, constants and variables so that deinit() is applied only to those unused instances.
  5. ARC maintains a ‘strong reference’ to those class instance property, constants and variables to restrict deallocation when the class instance is currently in use.

For more details about how ARC works go to Apple developer docs

1 Memory Management

Memory management is crucial in any application you’ll write. This is due to the hardware limitation in mobile devices.

1.1 The MRR

In the past Cocoa developers were responsible for memory managing resources, throughout MRR (Manual Retain Release).

Vehicle *cloudCar = [[Vehicle Alloc]int];
//..
NSString *model = cloudCar.model;
//..
[cloudCar release]

The alloc method was use to create an object and claim the ownership overhead and release was use to deallocated all ownership objects.

1.2 Automatic Reference Counting (ARC)

In 2011 Apple moves to Automatic Reference Counting. In most of cases memory management in swift just works and we don’t do anythings much about memory management. ARC automatically freeze up the memory use when class instances are no longer needed.

Each time of a class is instance, ARC allocate a pice of memory for that instance. This pice of memory contain the informations relevant to the type of instance.

var cloudCar = Vehicle()
var cloudCar = Vehicle()

Then if the instance not needed any longer , you could liberate the space allocated to anything else.

var cloudCar : Vehicle?
cloudCar = Vehicle()
cloudCar = nil

Off course if the cloudCar was deallocated from memory and you still use it, the application will crash. 💣🤗

var cloudCar : Vehicle?
cloudCar = Vehicle()
cloudCar = nil
label.text = cloudCar.name

To avoid this problem, ARC uses a references counter to track each of the reference of the class object. To every object instanced ARC increment the number of references to that object. When the object will removed the counter will decremented.

To avoid crashes ARC will not deallocated an instance as long is even one active reference still exist.

2 Strong Reference Cycles 🔒

As we have seen ARC is able to deallocate memory for us, however it’s possible to write code whit some strong references.

Strong Reference

2.1 Creating a new project

To get started, we are going to create a Single View Application iOS project.

And we go ahead to make new simple class named Vehicle:

  • A propriety type
  • An initialiser init()
  • A deinitialiser deinit()
//  Vehicle.swift
import Foundation
class Vehicle {
   let type: String
   init(type: String) {
      self.type = type
      print("\(type) is being initialised")
   }
   deinit {
      print("\(type) is being deinitialised")
   }
}
When the class Vehicle will be initialised the application will print the message “(type) is being initialised” and when Vehicle will be deinitialised will print “(type) is being deinitialised”. easy-peasy

So in the ViewController we’ll create two instances of Vehicle class and then run the application:

class ViewController: UIViewController {
   var reference1: Vehicle = Vehicle(kind: "CloudCar")
   var reference2: Vehicle? //optional type (?)
}

In the Output console you can see the reference1 message CloudCar is being initialised but you don’t see the reference2 because is optional type (?) and the value is nil. That’s not strong reference from reference2 to reference1.

Now we change our ViewController to create a Strong Reference.

In this code we create three Vehicle references, then we initialise the reference1 as Vehicle object and reference2, reference3 with reference1.

//  ViewController.swift
import UIKit
class ViewController: UIViewController {
   var reference1: Vehicle?
   var reference2: Vehicle?
   var reference3: Vehicle?
   var timer: Timer? 
   var count = 0
   override func viewDidLoad() {
      super.viewDidLoad()
      reference1 = Vehicle(kind: "CloudCar")
      reference2 = reference1
      reference3 = reference1

timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.step), userInfo: nil, repeats: true)
   }
   func step() {
      if count >= 3 {
         reference3 = nil
         reference2 = nil
         print("the reference remains!")
      }
      if count >= 5 {
         reference1 = nil
         print("our object is deallocated!")
      }
      count = count + 1
      print("\(count) Step")
}
}

Now run the application and check the output:

Through a timer script the application will set nil reference2 and reference3 at third step, but Vehicle object will not be deallocated!

CloudCar will be deallocated at fifth step when reference1 will set as nil and Vehicle will finally remove from the memory.

2.2 Frequently Mistake

In the following example we’ll see as create a frequently strong reference cycle mistake.

Now we add new class named Stormtrooper

//  Stormtrooper.swift
import Foundation
class Stormtrooper {
   let specialization: String
   var vehicle : Vehicle?
   init(specialization: String) {
      self.specialization = specialization
   }
   deinit {
      print("Stormtrooper \(specialization) is being deinitialized")
   }
}

In Vehicle class add stormtrooper option:

var stormtrooper: Stormtrooper?

And then we modify our ViewController

//  ViewController.swift
import UIKit
class ViewController: UIViewController {
   var seatrooper : Stormtrooper?
var submarine : Vehicle?
var toogle = false
let runButton = UIButton()
   override func viewDidLoad() {
       super.viewDidLoad()
       submarine = Vehicle(type: "Aquatic")
seatrooper = Stormtrooper(specialization: "Aquatic")
       submarine!.stormtrooper = seatrooper
seatrooper!.vehicle = submarine
       seatrooper = nil
submarine = nil
       // Create run button
runButton.frame = CGRect(x: 30, y: 30, width: 150, height: 30)
runButton.setTitle("Run", for: UIControlState.normal)
runButton.backgroundColor = UIColor.blue
runButton.addTarget(self, action: #selector(self.toggleButtonTapped), for: UIControlEvents.touchUpInside)
self.view.addSubview(runButton)

}
}

Now we can run the application and as you can see Vehicle was initialised but was not deinitialised! We have a memory leak problem!

In our ViewController we initialised an object Vehicle and another object Stormtrooper:

submarine = Vehicle(type: "Aquatic")
seatrooper = Stormtrooper(specialization: "Aquatic")

and then we both set nil

seatrooper = nil
submarine = nil

ARC doesn’t deinitialise the Vehicle object because we have created a strong reference cycle in the classes Vehicle and Stormtrooper:

To visualise, this is how strong reference works, we have create a recursive strong reference, and this had create a problem for our application!

strong reference cycle

3 Memory instruments

When you have a memory problem in swift, you can use the Xcode instruments to debug it easily.

3.1 Debug navigator tools

The debug tool monitor your running app. The detail report of each type of gauge differs in the specifics, but all follow a similar pattern.

On the top left click on the debug navigator and then double click on Memory tab.

Now click on Profile in Instruments and then click on Transfer button.

3.2 Instruments

In the Instruments windows the time line shows a memory leak problem thought the red X.

Then double click on leak Checks, you can see Vehicle and Stormtrooper in the Leaked Object list.

Now click on All Heap & Anonymous VM (1), then click on right the Display Settings button(2).

Go to simulator and in the our application click the blue Run button(3).

Back to instruments windows then click Mark Generation Button (4) and finally click Stop Recording button(5).

We have created a snapshot of memory status.

Expanding the list you can two items named ARC.Vehicle and ARC.Stormtrooper, select one (1) and click on details icon (2).

Now on the right column you can see three black icons, double click on one of those (3) to show the problematic code.

3.3 Finding Reference Cycles in Xcode 8

While the app is still running, move over to the bottom of Xcode and click the Debug Memory Graph button:

Observe the new kind of issues (warnings, errors, etc): Runtime Issues. They look like a purple square with a white exclamation mark inside, such as the one shown selected in this screenshot:

As you know, we two problems with Vehicle and Stormtrooper objects. So click on one those items and a simple chart will show the double reference.

4 Weak and Unowned References

4.1 Weak References

To break strong reference cycles, you can specify the relationship between reference objects as weak. Weak references don’t increase the strong reference count of the object. Unless otherwise specified, all references are strong. Weak references are always declared as optional types or nil and both objects must have independent lifetimes.

4.2 Unowned References

Another reference modifier you can use that doesn’t increase the reference count: unowned. What’s the difference between unowned and weak? A weak reference is always optional and automatically becomes nil when the referenced object deinitializes. That’s why you must define weak properties as optional var types for your code to compile. Unowned references are never optional types.

4.3 How to fix our memory problem

The weak keyword is obvious fix for our problem because vehicle and stormtrooper are two option variables.

class Stormtrooper {
//var vehicle : Vehicle?
weak var vehicle : Vehicle? //solution
}
class Vehicle {
//var stormtrooper : Stormtrooper?
weak var stormtrooper : Stormtrooper? //solution
}

Edit your code and test the application with all instruments, the memory leak problems are disappears. 💪🏻


Download source code

Check out the example source code on Github.


A good book to learn more

Mastering Swift 3


If you enjoyed this article please recommend and share.
This is the post you are looking for.
Like what you read? Give Enrico Piovesan a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.