Swift Automatic Reference Counting (ARC)
Swift uses Automatic Reference Counting (ARC) to track and manage your application's memory usage.
Typically, we do not need to manually release memory because ARC automatically frees up the memory used by class instances when those instances are no longer needed.
However, there are times when we still need to manage memory in our code.
ARC Features
-
Whenever a new instance of a class is created using the init() method, ARC allocates a chunk of memory to store information about that instance.
-
This memory holds the instance's type information and the values of all its properties.
-
When the instance is no longer needed, ARC frees up the memory it occupied, allowing the memory to be used for other purposes.
-
To ensure that active instances are not destroyed, ARC tracks and counts how many properties, constants, and variables are currently referencing each instance.
-
Assigning an instance to a property, constant, or variable creates a strong reference to that instance. As long as there is a strong reference, the instance is not allowed to be destroyed.
ARC Example
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
// Values are automatically initialized to nil, and no reference to a Person instance yet
var reference1: Person?
var reference2: Person?
var reference3: Person?
// Create a new instance of Person
reference1 = Person(name: "tutorialpro")
// Assign to other two variables, creating two more strong references
reference2 = reference1
reference3 = reference1
// Break the first strong reference
reference1 = nil
// Break the second strong reference
reference2 = nil
// Break the third strong reference and call the deinitializer
reference3 = nil
The output of the above program is:
tutorialpro is being initialized
tutorialpro is being deinitialized
Strong Reference Cycles Between Class Instances
In the example above, ARC tracks the number of references to the new Person instance you create and deallocates it when it is no longer needed.
However, it is possible to write code where a class instance never gets to a point where it has zero strong references. This happens when two class instances hold a strong reference to each other, causing each other to not be destroyed. This is known as a strong reference cycle.
Example
The following example demonstrates an unintended strong reference cycle that occurs between two class instances. It defines two classes: Person and Apartment, which model a building and its residents:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { print("Apartment #\(number) is being deinitialized") }
}
// Both variables are initialized to nil
var tutorialpro: Person?
var number73: Apartment?
// Assign values
tutorialpro = Person(name: "tutorialpro")
number73 = Apartment(number: 73)
// The exclamation marks are used to unwrap and access the instances inside the optional variables tutorialpro and number73
// A strong reference cycle is created
tutorialpro!.apartment = number73
number73!.tenant = tutorialpro
// When the strong references from the tutorialpro and number73 variables are broken, the reference count does not drop to zero, and the instances are not deallocated by ARC
// Note that no deinitializers are called when you set these two variables to nil
// The strong reference cycle prevents the deallocation of the Person and Apartment instances, causing a memory leak in your application
tutorialpro = nil
number73 = nil
Resolving Strong Reference Cycles Between Class Instances
Swift provides two ways to resolve strong reference cycles when you work with properties of class type:
Weak References
Unowned References
Weak and unowned references allow one instance in a cycle to reference the other without keeping a strong hold on it. This allows instances to reference each other without creating a strong reference cycle. Use weak references for instances that will become nil during their lifecycle. Conversely, use unowned references for instances that will never be assigned nil after initialization.
Weak Reference Example
class Module {
let name: String
init(name: String) { self.name = name }
var sub: SubModule?
deinit { print("\(name) main module") }
}
class SubModule {
let number: Int
init(number: Int) { self.number = number }
weak var topic: Module?
deinit { print("Submodule topic number is \(number)") }
}
var toc: Module?
var list: SubModule?
toc = Module(name: "ARC")
list = SubModule(number: 4)
toc!.sub = list
list!.topic = toc
toc = nil
list = nil
The output of the above program is:
ARC main module
Submodule topic number is 4
Unowned Reference Example
class Student {
let name: String
var section: Marks?
init(name: String) {
self.name = name
}
deinit { print("\(name)") }
}
class Marks {
let marks: Int
unowned let stname: Student
init(marks: Int, stname: Student) {
self.marks = marks
self.stname = stname
}
deinit { print("Student's score is \(marks)") }
}
var module: Student?
module = Student(name: "ARC")
module!.section = Marks(marks: 98, stname: module!)
module = nil
The output of the above program is:
ARC
Student's score is 98
Strong Reference Cycles for Closures
Strong reference cycles can also occur when you assign a closure to a property of a class instance, and the body of that closure captures the instance. This can happen if the closure accesses an instance property, such as self.someProperty
, or calls an instance method, such as self.someMethod
. Both of these actions capture self
and create a strong reference cycle.
Example
The following example demonstrates how a strong reference cycle is created when a closure captures self
. It defines a class called HTMLElement
, which represents a single element in HTML:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
// Create an instance and print the information
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
The HTMLElement
class creates a strong reference cycle between the instance and the closure assigned to asHTML
.
The asHTML
property of the instance holds a strong reference to the closure. However, the closure uses self
within its body (referencing self.name
and self.text
), thus the closure captures self
, which means the closure in turn holds a strong reference to the HTMLElement
instance. This creates a strong reference cycle between the two objects.
To resolve the strong reference cycle caused by closures: Define a capture list as part of the closure's definition to break the strong reference cycle between the closure and the class instance.
Weak and Unowned References
When a closure and the captured instance always reference each other and are always deallocated together, define the capture inside the closure as an unowned reference.
Conversely, when the captured reference might sometimes be nil
, define the capture inside the closure as a weak reference.
If the captured reference will never be set to nil
, it should be an unowned reference instead of a weak one.
Example
In the previous HTMLElement
example, using an unowned reference is the correct way to resolve the strong reference cycle. This is how you can write the HTMLElement
class to avoid the strong reference cycle:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
// Create and print an HTMLElement instance
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// The HTMLElement instance will be deallocated, and you will see its deinitializer print a message
paragraph = nil
The output of the above program is:
<p>hello, world</p>
p is being deinitialized