Swift automatic reference count (ARC)


Release date:2023-12-02 Update date:2023-12-08 Editor:admin View counts:263

Label:

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

Powered by TorCMS (https://github.com/bukun/TorCMS).