Swift automatic reference counting mechanism ARC, swift counting mechanism arc

Source: Internet
Author: User

Swift automatic reference counting mechanism ARC, swift counting mechanism arc

Swift uses the automatic reference count (ARC) mechanism to track and manage the memory of your application. In general, Swift's memory management mechanism will always play a role, and you do not have to consider memory management on your own. ARC Automatically releases the memory occupied by class instances when they are no longer used.

 

However, in a few cases, ARC needs more information about the relationship between your code to help you manage the memory. This chapter describes these situations and demonstrates how to enable ARC to manage the memory of your application.

 

Note:

 

The reference count is only applied to instances of the class. Struct and enumeration types are value types, not reference types, nor stored and transmitted by reference.

 

Automatic reference counting mechanism

Each time you create a new instance of a class, ARC allocates a large block of memory to store instance information. The memory contains the instance type information and the values of all related attributes of the instance. In addition, when the instance is no longer used, ARC releases the memory occupied by the instance and allows the released memory to be used for other purposes. This ensures that instances that are no longer in use do not occupy the memory space all the time.

 

However, when ARC revokes and releases an instance in use, its attributes and methods cannot be accessed or called. In fact, if you try to access this instance, your application may crash.

 

To ensure that the instances in use are not destroyed, ARC tracks and computes the attributes, constants, and variables that each instance is being referenced. Even if the instance is referenced by one, ARC will not destroy the instance.

 

To make it possible, no matter you assign an instance a value to an attribute, constant or variable, attribute, constant or variable, you will create a strong reference for this instance. It is called a strong reference because it will hold the instance firmly. As long as the strong reference is still in place, the instance cannot be destroyed.

 

 

Automatic reference counting practice

The following example shows the working mechanism of automatic reference counting. The example starts with a simple Person class and defines a constant attribute named name:

 

 

  class Person {       let name: String             init(name: String) {           self.name = name           println("\(name) is being initialized")       }            deinit {           println("\(name) is being deinitialized")       }    }

The Person class has a constructor that assigns values to the name attribute of the instance and prints information to indicate that the initialization process takes effect. The Person class also has destructor and prints information when the instance is destroyed.

 

The following code snippet defines three types: Person? Variable used to create multiple references for the new Person instance according to the order in the code snippet. Since these variables are defined as optional types (Person ?, Instead of "Person"), their values will be automatically initialized to nil, and will not be referenced to the Person class instance yet.

 

var reference1: Person?   var reference2: Person?   var reference3: Person?

Now you can create a new instance of the Person class and assign it to one of the three variables:

 

 reference1 = Person(name: "John Appleseed")   // prints "John Appleseed is being initialized”

Note that when you call the Person class constructor, "John Appleseed is being initialized" will be printed. It can be determined that the constructor is executed.

 

Because the new instance of the Person class is assigned the reference1 variable, a strong reference is created between the new instance of the reference1 to the Person class. Because of this strong reference, ARC will ensure that the Person instance is kept in the memory and not destroyed.

 

If you assign the same Person instance to the other two variables, the instance will have two more strong references:

 

   reference2 = reference1   reference3 = reference1


Now this Person instance has three strong references.

 

If you assign nil to two variables to disconnect two strong references (), including the first strong references), only one strong reference will be left, and the Person instance will not be destroyed:

 

reference2 = reference1reference3 = reference1


The ARC will destroy the Person instance in the third, that is, when the last strong reference is disconnected. This also means that you no longer use this Person instance:

 

reference3 = nil// prints "John Appleseed is beingdeinitialized"


 

Cyclic strong reference between class instances

In the above example, ARC will track the reference quantity of the Person instance you created and destroy it when the Person instance is no longer needed.

 

However, we may write such code, and a class will never have 0 strong references. This occurs when two class instances maintain strong references from each other and prevent the other instance from being destroyed. This is the so-called circular strong reference.

 

You can replace strong references by defining the relationship between classes as weak references or no primary references to solve the issue of circular strong references. The specific process is described in the circular strong reference between class instances. In any case, before you learn how to solve the problem of strong circular references, it is necessary to know how it is generated.

 

The following example shows how to generate a circular reference. The example defines two classes: Person and Apartment, used to model an Apartment and its residents:

 

class Person {   let name: String   init(name: String) { self.name = name }   var apartment: Apartment?   deinit { println("\(name) is being deinitialized") }} class Apartment {   let number: Int   init(number: Int) { self.number = number }   var tenant: Person?   deinit { println("Apartment #\(number) is beingdeinitialized") }}


Each Person instance has an attribute of the String type and name, and an optional apartment attribute initialized as nil. Apartment properties are optional because a person does not always own an apartment.

 

Similarly, each Apartment instance has an attribute called number, whose type is Int, and an optional tenant attribute initialized as nil. Tenant attributes are optional because an apartment does not always have residents.

 

Both Classes define destructor to output information when the class instance is destructed. This allows you to know whether the Person and Apartment instances are destroyed as expected.

 

The following code snippet defines two optional types of variables, john and number73, and is set to the following Apartment and Person instances respectively. Both variables are initialized to nil and are optional:

 

var john: Person?var number73: Apartment?

Now you can create a specific Person and Apartment instance and assign the class instance to the variables john and number73:

 

john = Person(name: "JohnAppleseed")number73 = Apartment(number: 73)


After the two instances are created and assigned values, the relationship between them is strongly referenced. The variable john now has a strong reference pointing to the Person instance, while the variable number73 has a strong reference pointing to the Apartment instance:

 

 

 

Now you can associate these two instances so that you can have an apartment and a tenant. Note that the exclamation point is used to expand and access the instances in the optional variables john and number73 so that the attributes of the instance can be assigned a value:

 

john!.apartment = number73number73!.tenant = john


Strong reference relationship after two instances are linked:

 

 

 

Unfortunately, after the two instances are joined together, a circular strong reference is created. The Person instance now has a strong reference pointing to the Apartment instance, and the Apartment instance also has a strong reference pointing to the Person instance. Therefore, when you disconnect the strong references held by variables john and number73, the reference count will not be reduced to 0, and the instance will not be destroyed by ARC:

 

john = nilnumber73 = nil


Note that when you set these two variables to nil, no destructor is called. A strong reference loop prevents the destruction of Person and Apartment instances and creates a memory leak in your application.

 

After you assign the values of john and number73 to nil, the strong reference relationships are as follows:

 

 

 

Strong reference relationships between Person and Apartment instances are retained and will not be disconnected.

 

 

Solve the circular and strong reference between instances

Swift provides two methods to solve the cyclic strong reference problem that you encounter when using class attributes: weak reference and unowned reference ).

 

Weak reference and no-master reference allow one instance in the circular reference to reference another instance without retaining strong reference. In this way, instances can reference each other without generating cyclic strong references.

 

Use Weak references for instances that change to nil in the life cycle. On the contrary, for instances that are no longer assigned a value to nil after initialization, there is no primary reference.

 

Weak reference

Weak references do not hold on to referenced instances and do not prevent ARC from destroying referenced instances. This action prevents reference from becoming a circular strong reference. When declaring attributes or variables, adding the weak keyword above indicates that this is a weak reference.

 

In the life cycle of an instance, if the reference does not have a value at some time, the weak reference can prevent the cyclic strong reference. If a reference always has a value, you can use a non-primary reference, which is described in a non-primary reference. In the Apartment example above, there are sometimes no "Residents" in the life cycle of an Apartment, so it is suitable to use weak references to solve the circular strong references.

 

Note:

 

The weak reference must be declared as a variable, indicating that its value can be modified at runtime. Weak references cannot be declared as constants.

Because weak references can have no value, you must declare each weak reference as an optional type. The optional type is the type recommended in the Swift language to indicate that there may be no value.

 

The weak reference does not keep the referenced instance. Even if the referenced instance exists, the instance may be destroyed. Therefore, ARC automatically assigns nil to the referenced instance after it is destroyed. You can check whether the weak reference value exists like other optional values, and you will never encounter an instance that has been destroyed but does not exist.

 

The example below is the same as the example of Person and Apartment above, but there is an important difference. This time, the tenant attribute of Apartment is declared as weak reference:

 

class Person {   let name: String   init(name: String) { self.name = name }   var apartment: Apartment?   deinit { println("\(name) is being deinitialized") }} class Apartment {   let number: Int   init(number: Int) { self.number = number }   weak var tenant: Person?   deinit { println("Apartment #\(number) is beingdeinitialized") }}


Then, as before, create a strong reference between two variables (john and number73) and associate them with two instances:

 

var john: Person?var number73: Apartment? john = Person(name: "JohnAppleseed")number73 = Apartment(number: 73) john!.apartment = number73number73!.tenant = john


Now, the reference relationships between the two associated instances are shown in:

 

 

 

The Person instance still has strong reference to the Apartment instance, but the Apartment instance only has weak reference to the Person instance. This means that when you disconnect the strong reference maintained by the john variable, there will no longer be a strong reference pointing to the Person instance:

 

 

 

Since there is no strong reference to the Person instance, the instance will be destroyed:

 

john = nil// prints "John Appleseed is beingdeinitialized"


The only remaining strong reference pointing to the Apartment instance comes from the variable number73. If you disconnect this strong reference, there will no longer be a strong reference pointing to the Apartment instance:

 

 

 

Since there is no strong reference to the Apartment instance, the instance will also be destroyed:

 

number73 = nil// prints "Apartment #73 is beingdeinitialized"


The above two sections of Code show that after the variables john and number73 are assigned as nil, The destructor of the Person instance and the Apartment instance both print the "Destroy" information. This proves that the reference loop is broken.

 

No primary reference

Similar to weak references, no primary reference does not hold the referenced instance firmly. Unlike weak references, a non-master reference always has a value. Therefore, a non-primary reference is always defined as a non-optional type (non-optional type ). When declaring attributes or variables, you can add the keyword unowned to indicate that this is a non-primary reference.

 

Since no primary reference is optional, you do not need to expand it when using it. No primary reference can always be accessed directly. However, ARC cannot set the primary reference to nil after the instance is destroyed, because non-optional variables cannot be assigned a value of nil.

 

Note: If you attempt to access the instance without a primary reference after the instance is destroyed, a runtime error is triggered. When using a non-primary reference, you must ensure that the reference always points to an instance that has not been destroyed.

 

It should also be noted that if you try to access an instance that has been destroyed and has no primary reference, the program will crash directly without unexpected behavior. So you should avoid such a situation.

The following example defines two classes: Customer and CreditCard, which simulate the credit card of the bank Customer and the Customer. Each of these two classes uses the instance of another class as its own attribute. This kind of relationship may create a loop of strong references.

 

The relationship between Customer and CreditCard is different from that between Apartment and Person in the previous weak reference example. In this data model, a customer may have or does not have a credit card, but a credit card is always associated with a customer. To represent this relationship, the Customer class has an optional type of card attribute, but the CreditCard class has a non-optional type of customer attribute.

 

In addition, you can create a CreditCard instance only by passing a number value and a customer instance to the CreditCard constructor. This ensures that a customer instance is always associated with the CreditCard instance.

 

Because a credit card is always associated with a customer, the customer attribute is defined as a non-primary reference to avoid repeated strong references:

 

class Customer {   let name: String   var card: CreditCard?   init(name: String) {       self.name = name    }   deinit { println("\(name) is being deinitialized") }} class CreditCard {   let number: Int   unowned let customer: Customer   init(number: Int, customer: Customer) {       self.number = number       self.customer = customer    }   deinit { println("Card #\(number) is being deinitialized") }}


The following code snippet defines an optional type of Customer variable named john, which is used to save the reference of a specific Customer. The variable is initialized to nil because it is optional.

 

var john: Customer?


Now you can create an instance of the Customer class, use it to initialize the CreditCard instance, and assign the newly created CreditCard instance to the Customer's card attribute.

 

john = Customer(name: "JohnAppleseed")john!.card = CreditCard(number:1234_5678_9012_3456, customer: john!)


After you associate two instances, their reference relationships are shown in:

 

 

 

The Customer instance holds a strong reference to the CreditCard instance, while the CreditCard instance holds a master reference to the Customer instance.

 

Because the customer has no primary reference, when you disconnect the strong reference held by the john variable, there will no longer be a strong reference pointing to the Customer instance:

 

 

 

Since there is no strong reference to the Customer instance, the instance is destroyed. Afterwards, there is no strong reference to the CreditCard instance, and the instance is also destroyed:

 

john = nil// prints "John Appleseed is beingdeinitialized"// prints "Card #1234567890123456 isbeing deinitialized"


The final Code shows that after the john variable is set to nil, the constructor of the Customer instance and the CreditCard instance both print the "Destroy" information.

 

Optional attributes of no primary reference and explicit expansion

The above examples of weak references and no primary references cover two common scenarios that require breaking the strong circular references.

 

The example of "Person" and "Apartment" shows that the values of both attributes are allowed to be nil and may generate cyclic strong references. This scenario is most suitable for weak references.

 

The example of Customer and CreditCard shows that the value of one attribute can be nil, while the value of another attribute cannot be nil, which may generate a strong cyclic reference. This scenario is most suitable for solving with no primary reference.

 

However, there is a third scenario in which both attributes must have values and cannot be nil after initialization. In this scenario, one class needs to use the non-primary attribute, and the other class uses the display expanded optional attribute.

 

This enables the two attributes to be directly accessed after initialization (you do not need to expand them), and avoids loop reference. This section will show you how to establish this relationship.

 

The following example defines two classes, Country and City. Each class saves the instances of another class as attributes. In this model, each country must have a capital, and each city must belong to one country. To implement this relationship, the Country class has a capitalCity attribute, while the City class has a country attribute:

 

class Country {   let name: String   let capitalCity: City!   init(name: String, capitalName: String) {       self.name = name       self.capitalCity = City(name: capitalName, country: self)    }} class City {   let name: String   unowned let country: Country   init(name: String, country: Country) {       self.name = name       self.country = country    }}


To establish the dependency between the two classes, the City constructor has a Country instance parameter and saves the instance as the country attribute.

 

The Country constructor calls the City constructor. However, only after the Country instance is fully initialized can the Country constructor pass self to the City constructor. (The two-step constructor has a specific description)

 

To meet this requirement, add an exclamation point (City!) at the end of the type !) The Country capitalCity attribute is declared as an optional type attribute for display expansion. This means that, like other optional types, the default value of the capitalCity attribute is nil, but it can be accessed without expanding its value. (There is a description in the optional type of display expansion)

 

Since the default value of capitalCity is nil, once the Country instance assigns a value to the name attribute in the constructor, the entire initialization process is complete. This means that once the name attribute is passed, the Country constructor can reference and pass the explicit self. When the Country constructor assigns a value to capitalCity, it can pass self as a parameter to the constructor of City.

 

The significance of the preceding statement is that you can use a statement to create both Country and City instances without generating cyclic strong references, and the capitalCity attributes can be directly accessed, you do not need to use an exclamation point to expand its optional values:

 

var country = Country(name:"Canada", capitalName: "Ottawa")println("\(country.name)'s capitalcity is called \(country.capitalCity.name)")// prints "Canada's capital city iscalled Ottawa"


In the preceding example, the use of show to show the optional values satisfies the needs of two class constructors. After the capitalCity attribute is initialized, it can be used as a non-optional value, and the colleague also avoids loop strong reference.

 

 

Loop strong reference caused by closures

We have seen that the loop strong reference ring is generated when the two class instance attributes maintain each other's strong reference. We also know how to use the weak reference and no primary reference to break the loop strong reference.

 

Strong circular references also occur when you assign a closure to an attribute of the class instance, and the closure body uses an instance. This closure body may have accessed a certain attribute of the instance, such as self. someProperty, or a method called by the closure, such as self. someMethod. Both cases cause the closure to "capture" self and generate a circular strong reference.

 

Strong circular references are generated because closures are similar to classes and are reference types. When you assign a closure to an attribute, you also assign a reference to the closure. Essentially, this is the same as the previous issue-the two strong references keep each other valid. However, unlike the two class instances, this time one is a class instance and the other is a closure.

 

Swift provides an elegant way to solve this problem, called the closuer capture list ). Similarly, before learning how to use a closure to occupy a list and destroy a circular strong reference, let's take a look at how a circular strong reference is generated, which is very helpful to us.

 

The following example shows how to generate a circular strong reference when a closure references self. The example defines a class called HTMLElement and uses a simple model to represent a separate 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 {       println("\(name) is being deinitialized")    } }


The HTMLElement class defines a name attribute to indicate the name of this element, for example, "p" for a section or "br" for a line break ". HTMLElement also defines an optional attribute text to set and display the text of HTML elements.

 

In addition to the above two attributes, HTMLElement also defines a lazy attribute asHTML. This attribute references a closure that combines name and text into HTML string fragments. This attribute is of the ()-> String type, or can be understood as "a function that returns a String without Parameters ".

 

By default, the closure is assigned to the asHTML attribute, which returns a string representing the HTML Tag. If the text value exists, the label contains the optional text value. If the text value does not exist, the label does not contain the text. For paragraph elements, the closure returns "<p> some text </p>" or "<p/>" based on whether the text is "some text" or nil ".

 

You can name and use the asHTML attribute like the instance method. However, because asHTML is a closure rather than an instance method, if you want to change the HTML processing of a specific element, you can replace the default value with a custom closure.

 

Note:

 

AsHTML is declared as the lazy attribute, because asHTML is required only when the element needs to be processed as a string output by HTML. That is to say, you can use self in the default closure, because the lazy attribute can be accessed only after Initialization is complete and self exists.

The HTMLElement class only provides one constructor. It initializes an element through the name and text (if any) parameters. This class also defines a destructor. When the HTMLElement instance is destroyed, a message is printed.

 

The following code shows how to use the HTMLElement class to create an instance and print messages.

 

var paragraph: HTMLElement? =HTMLElement(name: "p", text: "hello, world")println(paragraph!.asHTML())// prints"hello, world"


Note:

 

The preceding paragraph variable is defined as an optional HTMLElement, so we can assign a value to nil to demonstrate loop strong reference.

Unfortunately, the HTMLElement class written above generates a circular strong reference between the class instance and the closure of the asHTML default value. As shown in figure:

 

 

 

The asHTML attribute of the Instance holds the strong reference of the closure. However, the closure uses self (referencing self. name and self. text), so the closure occupies the self, which means that the closure in turn holds the strong reference of the HTMLElement instance. In this way, the two objects generate a circular strong reference. (

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.