16 Automatic Reference counting
ARC
Swift uses automatic reference counting (ARC) to manage the memory usage of the application. This means that memory management is already part of Swift, and in most cases you do not need to consider memory management. When instances are no longer needed,arc automatically frees the memory used by these instances.
However, in a few cases, you have to provide additional information about the code to ARC so that it can help you manage this part of the memory. This chapter explains these scenarios and shows how to use arc to manage the memory of your application.
Attention
The reference count acts only on the class instance. Structs and enumerations are value types, not reference types, so they cannot be referenced for storage and delivery.
1.How ARC works
Whenever you create an instance of a class,arc allocates a memory block to store the information for this instance, containing the type information and the attribute value information for the instance.
In addition, when the instance is no longer in use,arc releases the memory occupied by the instance, which can be used again.
However, if arc frees the instance that is being used, it can no longer access the instance properties, or invoke the method of the instance. Direct access to this instance can cause the application to crash.
To ensure that instances are present when an instance is required,arc tracks how many properties, constants, and variables point to these instances for each class instance. Arc does not release this instance when an active reference points to it.
To achieve this, when you assign a class instance to a property, constant, or variable, a strong reference to the instance (strong reference) is constructed. is called a strong reference because it holds this instance stably, and when the strong reference is present, the instance cannot be freed.
2. ARC Instance
The following example shows how arc works. Defines a simple class person that contains a store constant property name:
Class Person {Let name:string init (name:string) { self.name = name print (' \ (name) is being Initiali Zed ") } deinit { print (" \ (name) is being deinitialized ")} }
The person class has an initialization method to set properties name and print a message indicating the initialization process. There is also a destructor method that prints the information that the instance was freed.
The following code defines a three person ? Types of variables, which are used in subsequent code to set multiple references to a person instance. Because these variables are optional types (person?). ), they are automatically initialized to nil, and no person instances are applied .
var reference1:person?var reference2:person?var Reference3:person?reference1 = person (name: "John Appleseed")
Prints "John Appleseed is being initialized"
Note This message: "John Appleseed is being initialized", whichindicates that the constructor for the class person has been called.
This is a strong reference because the new person instance is assigned a value to the variable reference1. Because of the existence of a strong reference, arc guarantees that the person instance is not freed in memory.
If you assign this person instance to more variables, the corresponding number of strong references is established:
Reference2 = Reference1reference3 = Reference1
There are now three strong references to this person instance.
If you assign nil to two of these variables to cut off the two strong references (including the original reference), there is a strong reference that exists, so the person instance is not freed.
Reference1 = Nilreference2 = Nil
ARC does not release the person instance until the third strong reference is destroyed, so you cannot use the instance after that:
Reference3 = Nil
Prints "John Appleseed is being deinitialized"
3. Strong reference loops between class instances
In the example above,arc tracks the reference to the person instance and guarantees that it is released only after the person instance is no longer in use.
However, it is possible to write an instance of a class without a strong reference to the code that is pointing to it. Imagine a strong reference loop if two class instances have a strong reference to each other.
4. Resolving strong reference loops between class instances
Swift provides two ways to resolve strong reference loops between class instance properties: Weak and non-primary (unowned) references.
Weak and no-primary references make it possible for a reference loop to point to other instances in the loop without requiring a strong reference. Instances that are referenced to each other do not have to form a strong reference loop.
A weak reference is used when a reference may become nil at some point in the life cycle . Instead, use a no-primary reference when the reference is no longer nil when it is set during initialization .
Weak references
A weak reference does not maintain a strong hold on the object being referred to, and therefore does not prevent arc from reclaiming the reference instance. This feature guarantees that a reference is not part of a strong reference loop. Indicates that a reference to a weak reference is preceded by the keyword weak in the life attribute or variable .
No master reference
As with weak references, a no-master reference does not hold a strong reference to the instance. But unlike weak references, a non-primary reference usually has a value. Therefore, a no-master reference is not defined as an optional type. Indicates that a non-primary reference is preceded by the keyword unowned when the attribute or variable is declared .
Because a non-primary reference is not an optional type, you do not have to untie it whenever you use a no-master reference. No primary references are usually directly accessible. However, ARC cannot set the reference value to nil when no primary reference refers to the instance being disposed, because the non-optional type cannot be set to nil.
Attention
After a non-primary reference to the instance is freed, if you like to access the non-primary reference, a run-time error will be triggered (use no master reference only if you can confirm that a reference has been pointing to an instance). This situation in Swift also causes the application to crash, with some unpredictable behavior occurring, although you may have taken some precautions
The following example defines two classes,customer and CreditCard, that represent a bank customer and a credit card. The properties of these two classes each store each other's class instances. This relationship has a potentially strong reference loop.
In this case, a customer may or may not have a credit card, but a credit card must be held by a customer. Therefore, the class customer has an optional card familiar, and the class CreditCard has a non-optional customer attribute.
In addition, when you create an CreditCard instance, you must pass a value number and a customer instance to its constructor. This ensures that the credit card instance always has a customer associated with it.
Because credit cards are always held by one user, defining the customer property as a no-primary reference prevents a strong reference loop.
Class Customer {let name:string var card:creditcard? Init (name:string) { self.name = name } deinit {print ("\ (name) is being deinitialized")}}class Creditcar d {Let number:int unowned let Customer:customer init (number:int, Customer:customer) { Self.number = number Self.customer = Customer } deinit {print ("Card #\ (number) is being deinitialized")} }
The following code snippet defines the The customer type optional variable , John, is used to store a specific user's reference, which is the value of nil:
var john:customer?
You can now create a customer instance and initialize a new CreditCard instance to set the card property of the customer instance :
John = Customer (name: "John Appleseed") john!. Card = CreditCard (number:1234_5678_9012_3456, customer:john!)
The customer instance has a strong reference to the CreditCard instance, and the Creaditcard instance has a non-primary reference to the customer instance.
Because there is no primary reference, when you break the strong reference that the variable John holds, there is no longer a strong reference to the customer instance.
The instance is freed because there is no strong reference to the customer instance. Then, the strong reference to the CreditCard instance does not exist, so the instance is also freed:
John = Nil
Prints Card #1234567890123456 is being deinitialized
Prints John Appleseed is being deinitialized
The preceding code snippet shows the information that the customer instance and the CreditCard instance are refactored after the variable John is set to nil.
No primary reference and implicit unpacking optional properties?
However, all two properties are always valued and cannot be set to nil. In this case, it is common to federate the non-primary attribute of a class and the implicit boxing optional attribute (implicitly unwrapped optional property) of a class type.
This ensures that two properties can be accessed directly and that the reference loop is prevented.
5. Strong reference loops for closures
A strong reference loop may also exist when a closure is assigned to a property of an instance of a class, and the closure body captures the instance. The capture instance is a result of a closure that accesses the properties of an instance, like Self.someproperty, or invokes the method of an instance, like Self.somemethod (). In either case, this causes the closure to capture self , resulting in a strong reference loop.
The existence of this strong reference loop is because closures and classes are all reference types. When you assign a closure to a property, a reference is assigned to the closure. Essentially the same as the previous question-two strong references point to each other. However, unlike two class instances, this is a class with a closure.
Swift provides a graceful solution to this problem, which is the closure capture list. However, before learning how to break a strong reference loop through a closure capture list, it is important to understand how such loops are caused.
The following example shows how a strong reference loop is caused when you use a closure to refer to self. Defines a class named HtmlElement that models a separate element in an HTML document:
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") }}
This HtmlElement class defines a property name that represents the name of an element (for example, "P","BR"), and an optional property text that represents the value of the string of HTML elements to render on the page
In addition, a lazy attribute ashtml is defined . This property refers to a closure, which combines name and text to form an HTML code string. This property type is ()-> string, which means that a function does not require any arguments and returns a string value.
By default, theashtml property is assigned a closure that returns an HTML tag string. This tag contains the optional text value. For a paragraph, the closure returns "<p>some text</p>" or "<p/>", depending on whether the Text property is "some text" or nil.
The Ashtml property is named and used similar to an instance method, but because it is a closure property, if you want to render a particular HTML element, you can use another closure instead of the default value of the Ashtml property.
This HtmlElement class provides a single constructor, passing a name and a text parameter. Defines a destructor that prints the destructor information for a htmlelement instance.
Here's how to use the HtmlElement class to create and print a new instance:
var paragraph:htmlelement? = HtmlElement (name: "P", Text: "Hello, World") print (paragraph!. Ashtml ())
Print <p>hello, world</p>
Unfortunately, the implementation of the Htmlelemnt class written above will cause a strong reference loop between the HtmlElement instance and the default ashtml value used by the closure
The Ashtml property of an instance holds a strong reference to its closure, but because the closure refers to self (self.name and self.text mode) within its class, the closure captures the class itself, meaning it also holds the A reference to the HtmlElement instance. The strong reference loop is created.
If you set the paragraph variable value to nil, the strong reference to the HtmlElement instance is destroyed, and the instance and its closures are not refactored because of the strong reference loop:
Paragraph = nil
Note the hint information in the HtmlElement destructor is not printed, indicating that the HtmlElement instance is not being refactored.
6. Solve the strong reference loop of closures
A strong reference loop between closures and class instances can be resolved by defining a capture list as part of the closure. The capture list defines the rules for when one or more reference types are captured in the closure body. Like a strong reference loop that resolves between two class instances, you declare each capture reference as a weak reference or without a primary reference. Which definition to choose depends on the relationship between the other parts of the code
Defining a capture List
Each element in the capture list consists of a reference to a pair of weak/unowned keywords and class instances (selfor someinstance). These pairs are enclosed in square brackets and separated by good.
Place the capture list in front of the closure parameter list and return type (if provided):
Lazy var someclosure: (Int, String), String = { [unowned Self] (index:int, stringtoprocess:string), string In closure body goes here}
If the closures do not contain parameter lists and return values, they can be inferred from the context, put the capture list in front of the closure, followed by the keyword in :
Lazy var someclosure: () String = {[unowned self] in closure body goes here}
Weak references and no master references
When the closure and the instance always refer to each other and release simultaneously, the definition of the closure capture list is no master reference.
When the capture reference may be nil, define the capture list as a weak reference. A weak reference is typically an optional type, and is set to nil after the instance is disposed . This allows you to check if the instance exists in the closure.
In Example HtmlElement, you can use a no-master reference to solve a strong-reference loop problem, with its code:
Class HTMLElement1 {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") c19/>}}
this The htmlelement implementation Adds a capture list to the ashtml closure on a previous basis. Here, the capture list is [unowned Self], which means capturing itself as a non-primary reference rather than a strong reference.
var paragraph1:htmlelement1? = HTMLElement1 (name: "P", Text: "Hello, world!") Print (paragraph1!. Ashtml ())
Print <p>hello, world!</p>
At this point, the closure capture itself is a no-master reference and does not hold a strong reference to the capture HtmlElement instance. If you set paragraph's strong reference to nil, theHtmlElement instance is freed and can be seen from the destructor information:
PARAGRAPH1 = Nil
Print P is being deinitialized
Swift Learning notes (eight)