Swift automatic reference count (ARC)
Swift uses automatic reference counting (ARC) to track and manage application memory
Usually we don’t need to free memory manually because ARC automatically frees up memory when an instance of a class is no longer in use.
But sometimes we still need to implement memory management in our code.
ARC function
When each time you use
init()
method to create a new instance of a class, ARC allocates a large chunk of memory to store the instance’s information.The memory contains the type information of the instance, as well as the values of all related properties of the instance.
When the instance is no longer in use, ARC frees up the memory occupied by the instance and allows the freed memory to be diverted to other uses.
To ensure that instances in use are not destroyed, ARC tracks and calculates how many properties, constants, and variables each instance is being referenced.
Instance is assigned to a property, constant, or variable, all of which create a strong reference to the instance, and the instance is not allowed to be destroyed as long as the strong reference is there.
ARC instance
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) Start initialization")
}
deinit {
print("\(name) Deconstructed")
}
}
// The value will be automatically initialized to nil and will not be referenced to an instance of the Person class yet
var reference1: Person?
var reference2: Person?
var reference3: Person?
// Create a new instance of the Person class
reference1 = Person(name: "Runoob")
//Assign values to the other two variables, and this instance will have two additional 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 destructor function
Reference3=nil
The output of the above program execution is as follows:
Runoob initialization begins
Runoob is deconstructed
Cyclic strong references between class instances
In the above example, ARC will track your newly created Person
the number of references to the instance, and will be found in the Person
destroy the instance when it is no longer needed.
However, we might write code that a class will never have 0 strong references. This happens when two class instances maintain a strong reference to each other and keep each other from being destroyed. This is the so-called cyclic strong reference.
Example
An example of inadvertently generating loop strong references is shown below. The example defines two classes: Person
and Apartment
to model the apartment and its residents:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is deconstructed") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { print("Apartment #\(number) is deconstructed") }
}
//Both variables are initialized to nil
Var runoob: Person?
Var number73: Apartment?
//Assignment
Runoob=Person (name: "Runoob")
Number73=Apartment (number: 73)
//The exclamation mark is used to expand and access instances in the optional variables runoob and number73
//Circular strong reference created
Runoob Apartment=number73
Number73 Tenant=runoob
//When breaking the strong references held by runoob and number73 variables, the reference count does not drop to 0, and the instance is not destroyed by ARC
//Note that when you set these two variables to nil, none of the destructors are called.
//The strong reference loop prevents the destruction of instances of the Person and Apartment classes, and causes a memory leak in your application
Runoob=nil
Number73=nil
Resolve cyclic strong references between instances
Swift provides two ways to solve the loop strong reference problem you encounter when using the properties of a class:
Weak reference
No master reference
Weak references and unowned references allow one instance of a circular reference to reference another instance without maintaining a strong reference. In this way, instances can refer to each other without generatingcircular strong references.
For the lifecycle to become nil
uses weak references for an instance ofthe on the contrary, after initializing the assignment, it is no longer assigned to nil
, using no primary reference
Weak reference instance
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("The number of sub module topics 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 execution is as follows:
ARC main module
The number of sub module topics is 4
No primary reference instance
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("The 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 execution is as follows:
ARC
The student's score is 98
Loop strong reference caused by closure
A circular strong reference also occurs when you assign a closure to a property of a class instance, and the instance is used in the closure body. Some property of the instance may be accessed in this closure, such as self.someProperty
or a method of the instance is called in the closure, such as self.someMethod
. In both cases, the closure “captures” the self, resulting in a circular strong reference.
Example
The following example shows you when a closure references the self
. How to generate a loop strong reference after. An example is defined called HTMLElement
, represented by a simple model HTML
a single element:
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 information
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
HTMLElement
class produces class instances and asHTML
a loop strongreference between the closures of the default.
Instance of asHTML
property holds a strong reference to the closure. However, the closure uses self
(referenced by the self.name
and self.text
so the closure captures the self
, which means that the closure in turn holds the HTMLElement
a strong reference to the instance. In this way, the two objects produce a circular strong reference.
Solve the loop strong reference caused by the closure: define the capture list as part of the closure when defining the closure, which can solve the loop strong reference between the closure and the class instance.
Weak reference and non-primary reference
When closures and captured instances always refer to each other and are always destroyed at the same time, the capture within the closure is definedas an unowned reference.
Conversely, when a reference is captured, it may sometimes be nil
defines the capture within the closure as a weak reference.
If the captured reference is never set to nil
a headless reference should be used instead of a weak reference
Example
Front HTMLElement
in the example, no master reference is the correct way to solve the problem of circular strong reference. Write like this HTMLElement
class to avoid circular strong references:
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 deconstructed")
}
}
//Create and print HTMLElement instances
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// The HTMLElement instance will be destroyed and the message printed by its destructor will be visible
paragraph = nil
The output of the above program execution is as follows:
<p>hello, world</p>
p is deconstructed