Automatic reference counting ARC

Refer to Apple's official document Automatic Reference Counting

Swift使用自动引用计数(ARC)来跟踪和管理应用程序的内存使用情况。在大多数情况下,这意味着内存管理在Swift中“正常工作”,并且您不需要自己考虑内存管理。当这些实例不再需要时,ARC会自动释放类实例使用的内存。

但是,在少数情况下,ARC需要有关代码各部分之间关系的更多信息才能为您管理内存。本章将介绍这些情况,并说明如何使ARC能够管理所有应用程序的内存。在Swift中使用ARC非常类似于在Objective-C中使用ARC的过渡到ARC版本注释中描述的方法。

引用计数仅适用于类的实例。结构和枚举是值类型,而不是引用类型,不会通过引用存储和传递。
How ARC works

Every time a new instance of a class is created, ARC allocates a block of memory to store information about that instance. This memory holds information about the instance type and the values ​​of any stored properties associated with the instance.

Also, when an instance is no longer needed, ARC will free the memory used by that instance so that the memory can be used for other purposes. This ensures that class instances do not take up memory space when they are no longer needed.

However, if ARC were to release an instance that was still in use, it would no longer be possible to access the instance's properties, or call the instance's methods. In fact, your app is likely to crash if you try to access the instance.

To ensure that instances don't disappear while they're still needed, ARC keeps track of the number of properties, constants, and variables currently referencing each class instance. ARC will not deallocate an instance as long as at least one active reference to the instance remains.

In order to do this, whenever an instance of a class is assigned to a property, constant or variable, that property, constant or variable coerces a reference to that instance. This reference is called a "strong" reference because it remains firm in this instance, and it is not allowed to be reassigned as long as the strong reference remains.

ARC in action

Below is an example of how automatic reference counting works. This example starts with a simple class called Person, which defines a class called name:

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

The Person class has an initialization name property that sets the instance and displays a message indicating that initialization is in progress. The Person class also has a deinitializer that prints a message when an instance of the class is released.

The next code fragment defines three type variables Person?, which are used by Person to set multiple references to the new instance in subsequent code fragments. Since these variables are of an optional type (Person? is not Person), they will automatically be value-initialized with nil and do not currently reference a Person instance.

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

You can now create a new Person instance and assign it to one of the following three variables:

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

Note that the message "John Appleseed is being initialized" is printed when you call the initializer for the Person class. This confirms that initialization has occurred.

Since the new Person instance has been assigned to the reference1 variable, now reference1 has a strong reference to the new Person instance. Since there is at least one strong reference, ARC ensures that its Person is kept in memory and not freed.

If the same Person instance is assigned to two more variables, two stronger references to that instance are established:

reference2 = reference1
reference3 = reference1

There are now three strong references to this single Person instance.

If you destroy two of these strong references (including the original reference) by assigning nil to both variables, Person retains the single strong reference and does not free the instance:

reference1 = nil
reference2 = nil

ARC won't free the Person instance until the third and last strong reference is broken, at which point it's clear you're no longer using the Person instance:

reference3 = nil
// Prints "John Appleseed is being deinitialized"
Strong reference cycle between class instances

In the example above, ARC is able to keep track of the number of references to the new instance of Person you create, and release the instance when the Person is no longer needed.

However, it is possible to write code where an instance of a class never reaches the point where its strong reference is zero. If there is a strong association between two class instances, then each instance keeps the other alive. This is called a strong reference period.

Strong reference cycles can be resolved by defining certain relationships between classes as weak or unowned references instead of strong references. This process is described in Resolving Strong Reference Cycles Between Class Instances. However, before you learn how to resolve strong reference cycles, it is useful to know how to cause such cycles.

Below is an example of how to force the creation of a reference cycle. This example defines two classes, Apartment called Personand, that model a set of apartments and its inhabitants:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

Each Person instance initially has a name type attribute String and an optional apartment attribute nil. The apartment attribute is optional because a person may not always have an apartment.

Likewise, each Apartment instance has a property of type unit, String, and has the initial optional property of tenant nil. Tenant attributes are optional, as apartments may not always have tenants.

Both classes define a deinitializer which prints out the fact that an instance of the class is being initialized. This enables you to see if Person and Apartment are releasing instances as expected.

The next code snippet defines two variables of optional type, called john and unit4A, which will be set to the specific Apartment and Person instances below. Since nil is optional, both variables have initial values:

var john: Person?
var unit4A: Apartment?

You can now create specific Person and Apartment instances and assign these new instances to the john and unit4A variables:

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

Here's what a strong reference looks like after creating and assigning these two instances. The john variable now has a strong reference to the new Person instance unit4A, and the variable has a strong reference to the new instance of Apartment:

write picture description here

You can now link the two instances together so that the person owns an apartment and the apartment has a tenant. Note that the exclamation mark (!) is used to unwrap and access optional variables stored in the internal case, john and unit4A, so that the properties of these instances can be set to:

john!.apartment = unit4A
unit4A!.tenant = john

Here's the effect of strong references when these two instances are chained together:

write picture description here

Unfortunately, concatenating the two instances creates a strong reference cycle between them. The Person instance now has a strong reference to Apartment, and the Apartment instance has a strong reference to Person. So when you break the strong references held by variables john and unit4A the reference count doesn't drop to zero and the instance doesn't get deallocated by ARC:

john = nil
unit4A = nil

Note that neither deinitializer is called when both variables are set to nil. Strong reference cycles prevent Person and Apartment instances from being constantly freed, resulting in memory leaks in your application.

Here is the effect of setting the parameter john and the unit4A variable to nil: strong reference:

write picture description here

The strong reference between the Person instance and the Apartment instance still exists and cannot be destroyed.

Resolving strong reference cycles between class instances

When you use properties of class types, Swift provides two ways to resolve strong reference cycles: weak references and unowned references.

Weak and unowned references enable one instance in a reference cycle to refer to another instance without maintaining a strong hold on it. These instances can then reference each other without creating a strong reference cycle.

Weak references are used when the other instance has a shorter lifetime - that is, when the other instance can be released first. In the Apartment example above, it would be appropriate for the apartment to be able to have no tenants at some point in its life cycle, so in this case a weak reference is an appropriate way to break the reference cycle. Instead, use an unowned reference when another instance has the same lifetime or a longer lifetime.

weak reference

A weak reference is a reference that doesn't enforce retention of the instance it references, so doesn't stop ARC from processing the referenced instance. This behavior prevents the reference from being part of a strong reference cycle. Weak references are indicated by placing the weak keyword before a property or variable declaration.

Because a weak reference does not strongly reference the instance it refers to, the instance can be deallocated while the weak reference still points to it. Therefore, ARC automatically sets a weak reference to nil when the instance it refers to is deallocated. Also, because weak references need to allow their value nil to change at runtime, they are always declared as variables of optional type rather than constants.

You can check for the existence of a value in a weak reference, just like any other optional value, and never refer to a reference to an invalid instance that no longer exists.

Note that
when ARC sets a weak reference, the property observer nil is not called.

The example below is the same Person and Apartment example from above, with one important difference. This time, the tenant property of the Apartment type is declared as a weak reference:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

Strong references to the two variables (john and unit4A) and the link between the two instances are created as before:

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

Here's how the reference looks to link the two instances together:

write picture description here
The Person instance still has a strong reference to the instance of Apartment, but the Apartment instance now has a weaker reference to the instance of Person. This means that when john breaks the strong reference the variable has by setting it to nil, that Person instance is no longer strongly referenced:

john = nil
// Prints "John Appleseed is being deinitialized"

Since there is no stronger reference to the Person instance, it is deallocated and the tenant property is set to nil:

write picture description here

The only remaining strong reference to Apartment comes from the unit4A variable. If you break this strong reference, then there is no stronger reference to the Apartment instance:

unit4A = nil
// Prints "Apartment 4A is being deinitialized"

It is also freed because there is no stronger reference to the Apartment instance:

write picture description here

Note
In systems that use garbage collection, weak pointers are sometimes used to implement simple caching mechanisms, since objects without strong references are only freed when memory pressure triggers garbage collection. However, with ARC, values ​​are freed as soon as the last strong reference is removed, making weak references unsuitable for this purpose.

unowned reference

Just like a weak reference, an unowned reference does not hold the instance it refers to. Unlike weak references, however, unowned references are used when another instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before the property or variable declaration.

It is expected that an unowned reference will always have a value. Therefore, ARC will never set the value of an unowned reference to nil, which means that the unowned reference is defined with a non-option type.

Important Use unowned references
only if you are sure that the reference always refers to an instance that is not freed.
A runtime error occurs if you attempt to access the value of an unowned reference after the instance has been deallocated.

The following example defines two classes, Customer and CreditCard which model is the bank customer and the credit card that the customer may use. Both of these classes store an instance of the other class as a property. This relationship has the potential to create a powerful reference cycle.

The relationship between Customer and CreditCard distance is slightly different between Apartment and Person as shown in the weak reference example above. In this data model, the customer may or may not have a credit card, but the credit card will always be associated with the customer. A CreditCard instance never goes beyond the Customer it refers to. To represent this, the Customer class has an optional card property, but the CreditCard class has a non-owning (and non-optional) customer property.

Additionally, new CreditCard instances can only be created by passing a number value and a customer instance to the custom CreditCard initializer. This ensures that the CreditCard instance always has the customer's CreditCard instance associated with it when the instance is created.

Since a credit card will always have a customer, you define its customer property as an unowned reference to avoid strong reference cycles:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

Note that
the number property of the CreditCard class is defined with a type of UInt64 instead of Int, to ensure that the number property is large enough to store 16-bit card numbers on 32-bit and 64-bit systems.

The next code snippet defines an optional variable named Customer named john that will be used to store a reference to a specific customer. As optional, the initial value of this variable is nil:

var john: Customer?

You can now create a Customer instance and use it to initialize and assign a new CreditCard instance as the customer's card property:

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

Here's what the quote looks like, with two instances now linked:
write picture description here

The Customer instance now has a strong reference CreditCard to the instance, and the CreditCard instance holds an unowned reference Customer to the instance.

Because of the unowned customer reference, when you break the strong reference held by the john variable, there is no stronger reference to the Customer instance:

write picture description here

It is freed because there is no stronger Customer instance reference. After this happens, there is no stronger reference to the CreditCard instance, it is also freed:

john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"

The last piece of code above shows that the deinitializers for both the Customer instance and the CreditCard instance print their "uninitialized" message after the john variable is set to nil.

Note The
above example shows how to use safe unowned references. Swift also provides unsafe unowned references for cases where runtime security checks need to be disabled - for example, for performance reasons. As with all unsafe operations, you are responsible for checking the safety of this code.
You indicate by writing that an unsafe unowned reference is unowned(unsafe). If you try to access an unsafe unowned reference after the instance it references is deallocated, the program will try to access the memory location where the instance once existed, which is an unsafe operation.

Unowned references and implicitly unwrapped optional properties

The above examples of weak and unowned references cover two of the more common scenarios where it is necessary to break a strong reference cycle.

Both properties, both of which are allowed to be nil, have the potential to cause strong base cycles in the case shown by the Person and Apartment examples. This situation is best solved with weak references.

In the cases shown in the Customer and CreditCard examples, being allowed to be nil for one property cannot be nil with another property potentially creating a strong reference cycle. This situation is best resolved with an unowned reference.

However, there is a third case where both properties should always have a value and neither should have nil once initialized. In this case, it can be useful to combine a non-owned property of one class with an implicitly unwrapped optional property of another class.

This enables direct access to both properties (without optional unpacking) once initialization is complete, while still avoiding reference cycles. This section will show you how to build that relationship.

The following example defines two classes, Country and City, each of which stores the other class as an instance of a property. In this data model, every country must have a capital, and every city must belong to a country. To represent this, the Country class has a capitalCity property, and the City class has a country property:

class Country {
    let name: String
    var 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 interdependence between the two classes, the initializer City takes a Country instance and stores it in its country property.

The initializer for City is called Country in the initializer. However, the initializing Country program cannot pass self to the City initializer until the new Country instance is fully initialized, as described in Two-Phase Initialization.

To handle this requirement, you declare the capitalCity property Country to be an implicitly unwrapped optional property, denoted by an exclamation mark at the end of its type annotation (City!). This means that the capitalCity property defaults to nil as any other optional value, but can be accessed without unwrapping its value as described in Implicitly unwrapped options.

Since capitalCity has a default nil value, Country treats new instances as fully initialized as long as the Country instance name has its property set in its initializer. This means that the Country initializer can start referencing and passing the implicit self property name once the property is set. The Country can therefore be initialized by passing self as one of the parameters City when initializing the Country's initialization by setting its own capitalCity property.

All of this means that you can create Country and City instances in a single statement without creating a strong reference cycle, and capitalCity can directly access the property without using an exclamation mark to open its optional value:

var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// Prints "Canada's capital city is called Ottawa"

In the example above, using an implicitly unwrapped optional means that all two-phase class initializer requirements are met. Once capitalCity is initialized, the property can be used and accessed like a non-optional value, while avoiding strong reference cycles.

Strong Reference Cycles for Closures

You saw above how a strong reference cycle is created when two class instance properties are strongly associated with each other. You also saw how to use weak and unowned references to break these strong reference cycles.

A strong reference cycle can also occur if a closure is assigned to a property of a class instance, and the body of that closure captures the instance. This capture can happen because the body of the closure accesses some property self.someProperty of the instance, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the close to "capture" self, creating a strong reference cycle.

This powerful reference cycle happens because closures (like classes) are reference types. When you assign a closure to a property, you are assigning a reference to the closure. Essentially, this is the same problem as above - two strong references keep each other alive. However, this time instead of two class instances, one class instance and closure that keeps each other alive.

Swift provides an elegant solution to this problem called closure capture lists. However, before you learn how to use a closure capture list to break a strong reference cycle, it is useful to know how to cause such a cycle.

The following example shows how to create a strong reference cycle self using a referenced closure. This example defines a class called HTMLElement that provides a simple model for a single 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")
    }

}

The HTMLElement class defines a name attribute that indicates the name of the element, such as "h1" for a heading element, "p" for a paragraph element, or "br" for a line break element. HTMLElement also defines an optional text property that you can set to a string representing the text to render within that HTML element.

In addition to these two simple properties, the HTMLElement class also defines a property called lazy asHTML. This attribute reference, combines the enclosing name and text into an HTML string fragment. The asHTML attribute is of type () -> String, or "a function that takes no arguments and returns a String value".

By default, the asHTML property is assigned a closure that returns a string representation of the HTML markup. The tag contains optional values ​​if the tag text exists, otherwise contains no text content (if text does not exist). For paragraph elements, enclosing will return "

some text

"or"

"Depends on whether the text property is equal to "some text" or nil.

The asHTML attribute is named and used somewhat like an instance method. However, because asHTML is a closure property and not an instance method, asHTML if you want to change the HTML rendering of a specific HTML element, you can use a custom closure to replace the default value of that property.

For example, asHTML If the text attribute is nil, you can set the attribute to default to a closure for some text, preventing the representation from returning empty HTML tags:

let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
// Prints "<h1>some default text</h1>"

Note that
the asHTML attribute is declared as a lazy attribute because it is only needed if the element actually needs to be rendered as a string value for some HTML output target. The fact that asHTML is a lazy property means that you can refer to self inside the default closure, because the lazy property won't be accessed until initialization is complete and self knows it exists.

Notice

该asHTML属性被声明为一个懒惰属性,因为只有在实际需要将元素呈现为某个HTML输出目标的字符串值时才需要该属性。事实asHTML是一个懒惰的属性意味着你可以self在默认的闭包内引用,因为在初始化完成并self知道存在之前,lazy属性将不会被访问。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"

Note that
the variable above paragraph is defined as an optional HTMLElement, so it can be set to nil below to demonstrate the existence of strong reference cycles

不幸的是,HTMLElement如上所述,该类在HTMLElement实例和用于默认asHTML值的闭包之间创建了强大的引用循环。这是周期的外观:

write picture description here

实例的asHTML属性对其关闭提供了强有力的参考。然而,因为封闭件是指self它的主体内(作为一种方式来引用self.name和self.text),封闭捕获自,这意味着它保持很强的参考回HTMLElement实例。两者之间建立了强大的参考循环。(有关在闭包中捕获值的更多信息,请参阅捕获值。)

Note
Although the closure refers to self multiple times, it captures only one strong reference to this HTMLElement instance.

如果您将该paragraph变量设置为nil并断开其对该HTMLElement实例HTMLElement的强引用,则由于强引用周期,实例及其闭包都不会被释放:
paragraph = nil
请注意,HTMLElementdeinitializer 中的消息未打印,这表明HTMLElement实例未被释放。
Solving closed-loop strong reference cycles
通过将捕获列表定义为闭包定义的一部分,可以解决闭包与类实例之间的强引用循环。捕获列表定义了在封闭体内捕获一个或多个引用类型时使用的规则。与两个类实例之间的强引用周期一样,您将每个捕获的引用声明为弱引用或无主引用,而不是强引用。弱或无主的适当选择取决于代码不同部分之间的关​​系。

Note that
Swift requires that every time you write self.someProperty or self.someMethod() (rather than just someProperty or someMethod() ) you refer to a member of the self closure. This helps you remember the possibility of accidental capture of self.

define a capture list
捕获列表中的每个项目都是weakor unowned关键字与对类实例(如self)的引用或使用某个值(例如delegate = self.delegate!)初始化的变量的配对。这些配对写在一对方括号内,用逗号分隔。

If they are provided, the capture list is placed before the closure's parameter list and return type:

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
如果闭包没有指定参数列表或返回类型,因为它们可以从上下文中推断出来,请将捕获列表放置在闭包的开头,然后是in关键字:
lazy var someClosure: () -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}
Weak and unowned references
当闭包和它捕获的实例将始终相互引用时,将闭包中的捕获定义为无主引用,并且将始终在同一时间解除分配。

相反,当捕获的参考可能nil在未来的某个点上时,将捕获定义为弱参考。弱引用始终是可选类型,并且nil在它们引用的实例被释放时自动变为。这使您可以检查封闭体内的存在。

Note
If a captured reference never becomes nil, it should always be captured as an unowned reference, not a weak reference.

无主的参考是用于解决在强基准周期的合适的捕获方法HTMLElement从例如用于瓶盖强参照循环以上。以下是你如何编写HTMLElement课程以避免循环:
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")
    }

}
HTMLElement除了在asHTML闭包中添加捕获列表之外, 此实现与以前的实现完全相同。在这种情况下,捕获列表是[unowned self]指“将自我捕获为无主引用而不是强引用”。

您可以HTMLElement像以前一样创建和打印实例:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"

Here's how references are used in capture lists:

write picture description here

这一次,self封闭的捕获是一个无主的参考,并且不会HTMLElement对它捕获的实例保持强有力的控制。如果您将paragraph变量的强引用设置为nil,那么该HTMLElement实例将被释放,如从下面示例中的deinitializer消息的打印中可以看到的那样:
paragraph = nil
// Prints "p is being deinitialized"

For more information on capture lists, see Capture Lists.

The machine translation is wrong, please compare the original version

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324790516&siteId=291194637
arc