1524 lines
46 KiB
Markdown
1524 lines
46 KiB
Markdown
# Automatic Reference Counting
|
|
|
|
Model the lifetime of objects and their relationships.
|
|
|
|
Swift uses *Automatic Reference Counting* (ARC)
|
|
to track and manage your app's memory usage.
|
|
In most cases, this means that memory management “just works” in Swift,
|
|
and you don't need to think about memory management yourself.
|
|
ARC automatically frees up the memory used by class instances
|
|
when those instances are no longer needed.
|
|
|
|
However, in a few cases ARC requires more information
|
|
about the relationships between parts of your code
|
|
in order to manage memory for you.
|
|
This chapter describes those situations
|
|
and shows how you enable ARC to manage all of your app's memory.
|
|
Using ARC in Swift is very similar to the approach described in
|
|
[Transitioning to ARC Release Notes](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html)
|
|
for using ARC with Objective-C.
|
|
|
|
Reference counting applies only to instances of classes.
|
|
Structures and enumerations are value types, not reference types,
|
|
and aren't stored and passed by reference.
|
|
|
|
## How ARC Works
|
|
|
|
Every time you create a new instance of a class,
|
|
ARC allocates a chunk of memory to store information about that instance.
|
|
This memory holds information about the type of the instance,
|
|
together with the values of any stored properties associated with that instance.
|
|
|
|
Additionally, when an instance is no longer needed,
|
|
ARC frees up the memory used by that instance
|
|
so that the memory can be used for other purposes instead.
|
|
This ensures that class instances don't take up space in memory
|
|
when they're no longer needed.
|
|
|
|
However, if ARC were to deallocate an instance that was still in use,
|
|
it would no longer be possible to access that instance's properties,
|
|
or call that instance's methods.
|
|
Indeed, if you tried to access the instance, your app would most likely crash.
|
|
|
|
To make sure that instances don't disappear while they're still needed,
|
|
ARC tracks how many properties, constants, and variables
|
|
are currently referring to each class instance.
|
|
ARC will not deallocate an instance
|
|
as long as at least one active reference to that instance still exists.
|
|
|
|
To make this possible,
|
|
whenever you assign a class instance to a property, constant, or variable,
|
|
that property, constant, or variable makes a *strong reference* to the instance.
|
|
The reference is called a "strong" reference because
|
|
it keeps a firm hold on that instance,
|
|
and doesn't allow it to be deallocated for as long as that strong reference remains.
|
|
|
|
## ARC in Action
|
|
|
|
Here's an example of how Automatic Reference Counting works.
|
|
This example starts with a simple class called `Person`,
|
|
which defines a stored constant property called `name`:
|
|
|
|
```swift
|
|
class Person {
|
|
let name: String
|
|
init(name: String) {
|
|
self.name = name
|
|
print("\(name) is being initialized")
|
|
}
|
|
deinit {
|
|
print("\(name) is being deinitialized")
|
|
}
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> 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 initializer that sets the instance's `name` property
|
|
and prints a message to indicate that initialization is underway.
|
|
The `Person` class also has a deinitializer
|
|
that prints a message when an instance of the class is deallocated.
|
|
|
|
The next code snippet defines three variables of type `Person?`,
|
|
which are used to set up multiple references to a new `Person` instance
|
|
in subsequent code snippets.
|
|
Because these variables are of an optional type (`Person?`, not `Person`),
|
|
they're automatically initialized with a value of `nil`,
|
|
and don't currently reference a `Person` instance.
|
|
|
|
```swift
|
|
var reference1: Person?
|
|
var reference2: Person?
|
|
var reference3: Person?
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> var reference1: Person?
|
|
-> var reference2: Person?
|
|
-> var reference3: Person?
|
|
```
|
|
-->
|
|
|
|
You can now create a new `Person` instance
|
|
and assign it to one of these three variables:
|
|
|
|
```swift
|
|
reference1 = Person(name: "John Appleseed")
|
|
// Prints "John Appleseed is being initialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> reference1 = Person(name: "John Appleseed")
|
|
<- John Appleseed is being initialized
|
|
```
|
|
-->
|
|
|
|
Note that the message `"John Appleseed is being initialized"` is printed
|
|
at the point that you call the `Person` class's initializer.
|
|
This confirms that initialization has taken place.
|
|
|
|
Because the new `Person` instance has been assigned to the `reference1` variable,
|
|
there's now a strong reference from `reference1` to the new `Person` instance.
|
|
Because there's at least one strong reference,
|
|
ARC makes sure that this `Person` is kept in memory and isn't deallocated.
|
|
|
|
If you assign the same `Person` instance to two more variables,
|
|
two more strong references to that instance are established:
|
|
|
|
```swift
|
|
reference2 = reference1
|
|
reference3 = reference1
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> reference2 = reference1
|
|
-> reference3 = reference1
|
|
```
|
|
-->
|
|
|
|
There are now *three* strong references to this single `Person` instance.
|
|
|
|
If you break two of these strong references (including the original reference)
|
|
by assigning `nil` to two of the variables,
|
|
a single strong reference remains,
|
|
and the `Person` instance isn't deallocated:
|
|
|
|
```swift
|
|
reference1 = nil
|
|
reference2 = nil
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> reference1 = nil
|
|
-> reference2 = nil
|
|
```
|
|
-->
|
|
|
|
ARC doesn't deallocate the `Person` instance until
|
|
the third and final strong reference is broken,
|
|
at which point it's clear that you are no longer using the `Person` instance:
|
|
|
|
```swift
|
|
reference3 = nil
|
|
// Prints "John Appleseed is being deinitialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `howARCWorks`
|
|
|
|
```swifttest
|
|
-> reference3 = nil
|
|
<- John Appleseed is being deinitialized
|
|
```
|
|
-->
|
|
|
|
## Strong Reference Cycles Between Class Instances
|
|
|
|
In the examples above,
|
|
ARC is able to track the number of references to the new `Person` instance you create
|
|
and to deallocate that `Person` instance when it's no longer needed.
|
|
|
|
However, it's possible to write code in which an instance of a class
|
|
*never* gets to a point where it has zero strong references.
|
|
This can happen if two class instances hold a strong reference to each other,
|
|
such that each instance keeps the other alive.
|
|
This is known as a *strong reference cycle*.
|
|
|
|
You resolve strong reference cycles
|
|
by defining some of the relationships between classes
|
|
as weak or unowned references instead of as strong references.
|
|
This process is described in
|
|
<doc:AutomaticReferenceCounting#Resolving-Strong-Reference-Cycles-Between-Class-Instances>.
|
|
However, before you learn how to resolve a strong reference cycle,
|
|
it's useful to understand how such a cycle is caused.
|
|
|
|
Here's an example of how a strong reference cycle can be created by accident.
|
|
This example defines two classes called `Person` and `Apartment`,
|
|
which model a block of apartments and its residents:
|
|
|
|
```swift
|
|
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") }
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `referenceCycles`
|
|
|
|
```swifttest
|
|
-> 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") }
|
|
}
|
|
```
|
|
-->
|
|
|
|
Every `Person` instance has a `name` property of type `String`
|
|
and an optional `apartment` property that's initially `nil`.
|
|
The `apartment` property is optional, because a person may not always have an apartment.
|
|
|
|
Similarly, every `Apartment` instance has a `unit` property of type `String`
|
|
and has an optional `tenant` property that's initially `nil`.
|
|
The tenant property is optional because an apartment may not always have a tenant.
|
|
|
|
Both of these classes also define a deinitializer,
|
|
which prints the fact that an instance of that class is being deinitialized.
|
|
This enables you to see whether
|
|
instances of `Person` and `Apartment` are being deallocated as expected.
|
|
|
|
This next code snippet defines two variables of optional type
|
|
called `john` and `unit4A`,
|
|
which will be set to a specific `Apartment` and `Person` instance below.
|
|
Both of these variables have an initial value of `nil`, by virtue of being optional:
|
|
|
|
```swift
|
|
var john: Person?
|
|
var unit4A: Apartment?
|
|
```
|
|
|
|
<!--
|
|
- test: `referenceCycles`
|
|
|
|
```swifttest
|
|
-> var john: Person?
|
|
-> var unit4A: Apartment?
|
|
```
|
|
-->
|
|
|
|
You can now create a specific `Person` instance and `Apartment` instance
|
|
and assign these new instances to the `john` and `unit4A` variables:
|
|
|
|
```swift
|
|
john = Person(name: "John Appleseed")
|
|
unit4A = Apartment(unit: "4A")
|
|
```
|
|
|
|
<!--
|
|
- test: `referenceCycles`
|
|
|
|
```swifttest
|
|
-> john = Person(name: "John Appleseed")
|
|
-> unit4A = Apartment(unit: "4A")
|
|
```
|
|
-->
|
|
|
|
Here's how the strong references look after creating and assigning these two instances.
|
|
The `john` variable now has a strong reference to the new `Person` instance,
|
|
and the `unit4A` variable has a strong reference to the new `Apartment` instance:
|
|
|
|

|
|
|
|
You can now link the two instances together
|
|
so that the person has an apartment, and the apartment has a tenant.
|
|
Note that an exclamation point (`!`) is used to unwrap and access
|
|
the instances stored inside the `john` and `unit4A` optional variables,
|
|
so that the properties of those instances can be set:
|
|
|
|
```swift
|
|
john!.apartment = unit4A
|
|
unit4A!.tenant = john
|
|
```
|
|
|
|
<!--
|
|
- test: `referenceCycles`
|
|
|
|
```swifttest
|
|
-> john!.apartment = unit4A
|
|
-> unit4A!.tenant = john
|
|
```
|
|
-->
|
|
|
|
Here's how the strong references look after you link the two instances together:
|
|
|
|

|
|
|
|
Unfortunately, linking these two instances creates
|
|
a strong reference cycle between them.
|
|
The `Person` instance now has a strong reference to the `Apartment` instance,
|
|
and the `Apartment` instance has a strong reference to the `Person` instance.
|
|
Therefore, when you break the strong references held by
|
|
the `john` and `unit4A` variables,
|
|
the reference counts don't drop to zero,
|
|
and the instances aren't deallocated by ARC:
|
|
|
|
```swift
|
|
john = nil
|
|
unit4A = nil
|
|
```
|
|
|
|
<!--
|
|
- test: `referenceCycles`
|
|
|
|
```swifttest
|
|
-> john = nil
|
|
-> unit4A = nil
|
|
```
|
|
-->
|
|
|
|
Note that neither deinitializer was called
|
|
when you set these two variables to `nil`.
|
|
The strong reference cycle prevents the `Person` and `Apartment` instances
|
|
from ever being deallocated, causing a memory leak in your app.
|
|
|
|
Here's how the strong references look after you set
|
|
the `john` and `unit4A` variables to `nil`:
|
|
|
|

|
|
|
|
The strong references between the `Person` instance
|
|
and the `Apartment` instance remain and can't be broken.
|
|
|
|
## Resolving Strong Reference Cycles Between Class Instances
|
|
|
|
Swift provides two ways to resolve strong reference cycles
|
|
when you work with properties of class type:
|
|
weak references and unowned references.
|
|
|
|
Weak and unowned references enable one instance in a reference cycle
|
|
to refer to the other instance *without* keeping a strong hold on it.
|
|
The instances can then refer to each other without creating a strong reference cycle.
|
|
|
|
Use a weak reference when the other instance has a shorter lifetime ---
|
|
that is, when the other instance can be deallocated first.
|
|
In the `Apartment` example above,
|
|
it's appropriate for an apartment to be able to have
|
|
no tenant at some point in its lifetime,
|
|
and so a weak reference is an appropriate way to break the reference cycle in this case.
|
|
In contrast, use an unowned reference when the other instance
|
|
has the same lifetime or a longer lifetime.
|
|
|
|
<!--
|
|
QUESTION: how do I answer the question
|
|
"which of the two properties in the reference cycle
|
|
should be marked as weak or unowned?"
|
|
-->
|
|
|
|
### Weak References
|
|
|
|
A *weak reference* is a reference that doesn't keep a strong hold
|
|
on the instance it refers to,
|
|
and so doesn't stop ARC from disposing of the referenced instance.
|
|
This behavior prevents the reference from becoming part of a strong reference cycle.
|
|
You indicate a weak reference by placing the `weak` keyword
|
|
before a property or variable declaration.
|
|
|
|
Because a weak reference doesn't keep a strong hold on the instance it refers to,
|
|
it's possible for that instance to be deallocated
|
|
while the weak reference is still referring to it.
|
|
Therefore, ARC automatically sets a weak reference to `nil`
|
|
when the instance that it refers to is deallocated.
|
|
And, because weak references need to allow
|
|
their value to be changed to `nil` at runtime,
|
|
they're always declared as variables, rather than constants, of an optional type.
|
|
|
|
You can check for the existence of a value in the weak reference,
|
|
just like any other optional value,
|
|
and you will never end up with
|
|
a reference to an invalid instance that no longer exists.
|
|
|
|
> Note: Property observers aren't called
|
|
> when ARC sets a weak reference to `nil`.
|
|
|
|
<!--
|
|
- test: `weak-reference-doesnt-trigger-didset`
|
|
|
|
```swifttest
|
|
-> class C {
|
|
weak var w: C? { didSet { print("did set") } }
|
|
}
|
|
-> var c1 = C()
|
|
-> do {
|
|
-> let c2 = C()
|
|
-> assert(c1.w == nil)
|
|
-> c1.w = c2
|
|
<< did set
|
|
-> assert(c1.w != nil)
|
|
-> } // ARC deallocates c2; didSet doesn't fire.
|
|
-> assert(c1.w == nil)
|
|
```
|
|
-->
|
|
|
|
The example below is identical to the `Person` and `Apartment` example from above,
|
|
with one important difference.
|
|
This time around, the `Apartment` type's `tenant` property
|
|
is declared as a weak reference:
|
|
|
|
```swift
|
|
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") }
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `weakReferences`
|
|
|
|
```swifttest
|
|
-> 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") }
|
|
}
|
|
```
|
|
-->
|
|
|
|
The strong references from the two variables (`john` and `unit4A`)
|
|
and the links between the two instances are created as before:
|
|
|
|
```swift
|
|
var john: Person?
|
|
var unit4A: Apartment?
|
|
|
|
john = Person(name: "John Appleseed")
|
|
unit4A = Apartment(unit: "4A")
|
|
|
|
john!.apartment = unit4A
|
|
unit4A!.tenant = john
|
|
```
|
|
|
|
<!--
|
|
- test: `weakReferences`
|
|
|
|
```swifttest
|
|
-> 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 references look now that you've linked the two instances together:
|
|
|
|

|
|
|
|
The `Person` instance still has a strong reference to the `Apartment` instance,
|
|
but the `Apartment` instance now has a *weak* reference to the `Person` instance.
|
|
This means that when you break the strong reference held by
|
|
the `john` variable by setting it to `nil`,
|
|
there are no more strong references to the `Person` instance:
|
|
|
|
```swift
|
|
john = nil
|
|
// Prints "John Appleseed is being deinitialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `weakReferences`
|
|
|
|
```swifttest
|
|
-> john = nil
|
|
<- John Appleseed is being deinitialized
|
|
```
|
|
-->
|
|
|
|
Because there are no more strong references to the `Person` instance,
|
|
it's deallocated
|
|
and the `tenant` property is set to `nil`:
|
|
|
|

|
|
|
|
The only remaining strong reference to the `Apartment` instance
|
|
is from the `unit4A` variable.
|
|
If you break *that* strong reference,
|
|
there are no more strong references to the `Apartment` instance:
|
|
|
|
```swift
|
|
unit4A = nil
|
|
// Prints "Apartment 4A is being deinitialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `weakReferences`
|
|
|
|
```swifttest
|
|
-> unit4A = nil
|
|
<- Apartment 4A is being deinitialized
|
|
```
|
|
-->
|
|
|
|
Because there are no more strong references to the `Apartment` instance,
|
|
it too is deallocated:
|
|
|
|

|
|
|
|
> Note: In systems that use garbage collection,
|
|
> weak pointers are sometimes used to implement a simple caching mechanism
|
|
> because objects with no strong references are deallocated
|
|
> only when memory pressure triggers garbage collection.
|
|
> However, with ARC, values are deallocated
|
|
> as soon as their last strong reference is removed,
|
|
> making weak references unsuitable for such a purpose.
|
|
|
|
### Unowned References
|
|
|
|
Like a weak reference,
|
|
an *unowned reference* doesn't keep
|
|
a strong hold on the instance it refers to.
|
|
Unlike a weak reference, however,
|
|
an unowned reference is used when the other instance
|
|
has the same lifetime or a longer lifetime.
|
|
You indicate an unowned reference by placing the `unowned` keyword
|
|
before a property or variable declaration.
|
|
|
|
Unlike a weak reference,
|
|
an unowned reference is expected to always have a value.
|
|
As a result,
|
|
marking a value as unowned doesn't make it optional,
|
|
and ARC never sets an unowned reference's value to `nil`.
|
|
|
|
<!--
|
|
Everything that unowned can do, weak can do slower and more awkwardly
|
|
(but still correctly).
|
|
Unowned is interesting because it's faster and easier (no optionals) ---
|
|
in the cases where it's actually correct for your data.
|
|
-->
|
|
|
|
> Important: Use an unowned reference only when you are sure that
|
|
> the reference *always* refers to an instance that hasn't been deallocated.
|
|
>
|
|
> If you try to access the value of an unowned reference
|
|
> after that instance has been deallocated,
|
|
> you'll get a runtime error.
|
|
|
|
<!--
|
|
One way to satisfy that requirement is to
|
|
always access objects that have unmanaged properties through their owner
|
|
instead of keeping a reference to them directly,
|
|
because those direct references could outlive the owner.
|
|
However... this strategy really only works when the unowned reference
|
|
is a backpointer from an object up to its owner.
|
|
-->
|
|
|
|
The following example defines two classes, `Customer` and `CreditCard`,
|
|
which model a bank customer and a possible credit card for that customer.
|
|
These two classes each store an instance of the other class as a property.
|
|
This relationship has the potential to create a strong reference cycle.
|
|
|
|
The relationship between `Customer` and `CreditCard` is slightly different from
|
|
the relationship between `Apartment` and `Person`
|
|
seen in the weak reference example above.
|
|
In this data model, a customer may or may not have a credit card,
|
|
but a credit card will *always* be associated with a customer.
|
|
A `CreditCard` instance never outlives the `Customer` that it refers to.
|
|
To represent this, the `Customer` class has an optional `card` property,
|
|
but the `CreditCard` class has an unowned (and non-optional) `customer` property.
|
|
|
|
Furthermore, a new `CreditCard` instance can *only* be created
|
|
by passing a `number` value and a `customer` instance
|
|
to a custom `CreditCard` initializer.
|
|
This ensures that a `CreditCard` instance always has
|
|
a `customer` instance associated with it when the `CreditCard` instance is created.
|
|
|
|
Because a credit card will always have a customer,
|
|
you define its `customer` property as an unowned reference,
|
|
to avoid a strong reference cycle:
|
|
|
|
```swift
|
|
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") }
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferences`
|
|
|
|
```swifttest
|
|
-> 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: The `number` property of the `CreditCard` class is defined with
|
|
> a type of `UInt64` rather than `Int`,
|
|
> to ensure that the `number` property's capacity is large enough to store
|
|
> a 16-digit card number on both 32-bit and 64-bit systems.
|
|
|
|
This next code snippet defines an optional `Customer` variable called `john`,
|
|
which will be used to store a reference to a specific customer.
|
|
This variable has an initial value of nil, by virtue of being optional:
|
|
|
|
```swift
|
|
var john: Customer?
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferences`
|
|
|
|
```swifttest
|
|
-> var john: Customer?
|
|
```
|
|
-->
|
|
|
|
You can now create a `Customer` instance,
|
|
and use it to initialize and assign a new `CreditCard` instance
|
|
as that customer's `card` property:
|
|
|
|
```swift
|
|
john = Customer(name: "John Appleseed")
|
|
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferences`
|
|
|
|
```swifttest
|
|
-> john = Customer(name: "John Appleseed")
|
|
-> john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
|
|
```
|
|
-->
|
|
|
|
Here's how the references look, now that you've linked the two instances:
|
|
|
|

|
|
|
|
The `Customer` instance now has a strong reference to the `CreditCard` instance,
|
|
and the `CreditCard` instance has an unowned reference to the `Customer` instance.
|
|
|
|
Because of the unowned `customer` reference,
|
|
when you break the strong reference held by the `john` variable,
|
|
there are no more strong references to the `Customer` instance:
|
|
|
|

|
|
|
|
Because there are no more strong references to the `Customer` instance,
|
|
it's deallocated.
|
|
After this happens,
|
|
there are no more strong references to the `CreditCard` instance,
|
|
and it too is deallocated:
|
|
|
|
```swift
|
|
john = nil
|
|
// Prints "John Appleseed is being deinitialized".
|
|
// Prints "Card #1234567890123456 is being deinitialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferences`
|
|
|
|
```swifttest
|
|
-> john = nil
|
|
<- John Appleseed is being deinitialized
|
|
<- Card #1234567890123456 is being deinitialized
|
|
```
|
|
-->
|
|
|
|
The final code snippet above shows that
|
|
the deinitializers for the `Customer` instance and `CreditCard` instance
|
|
both print their “deinitialized” messages
|
|
after the `john` variable is set to `nil`.
|
|
|
|
> Note: The examples above show how to use *safe* unowned references.
|
|
> Swift also provides *unsafe* unowned references for cases where
|
|
> you need to disable runtime safety checks ---
|
|
> for example, for performance reasons.
|
|
> As with all unsafe operations,
|
|
> you take on the responsibility for checking that code for safety.
|
|
>
|
|
> You indicate an unsafe unowned reference by writing `unowned(unsafe)`.
|
|
> If you try to access an unsafe unowned reference
|
|
> after the instance that it refers to is deallocated,
|
|
> your program will try to access the memory location
|
|
> where the instance used to be,
|
|
> which is an unsafe operation.
|
|
|
|
<!--
|
|
<rdar://problem/28805121> TSPL: ARC - Add discussion of "unowned" with different lifetimes
|
|
Try expanding the example above so each customer has an array of credit cards.
|
|
-->
|
|
|
|
### Unowned Optional References
|
|
|
|
You can mark an optional reference to a class as unowned.
|
|
In terms of the ARC ownership model,
|
|
an unowned optional reference and a weak reference
|
|
can both be used in the same contexts.
|
|
The difference is that when you use an unowned optional reference,
|
|
you're responsible for making sure it always
|
|
refers to a valid object or is set to `nil`.
|
|
|
|
Here's an example that keeps track of the courses
|
|
offered by a particular department at a school:
|
|
|
|
```swift
|
|
class Department {
|
|
var name: String
|
|
var courses: [Course]
|
|
init(name: String) {
|
|
self.name = name
|
|
self.courses = []
|
|
}
|
|
}
|
|
|
|
class Course {
|
|
var name: String
|
|
unowned var department: Department
|
|
unowned var nextCourse: Course?
|
|
init(name: String, in department: Department) {
|
|
self.name = name
|
|
self.department = department
|
|
self.nextCourse = nil
|
|
}
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `unowned-optional-references`
|
|
|
|
```swifttest
|
|
-> class Department {
|
|
var name: String
|
|
var courses: [Course]
|
|
init(name: String) {
|
|
self.name = name
|
|
self.courses = []
|
|
}
|
|
}
|
|
|
|
-> class Course {
|
|
var name: String
|
|
unowned var department: Department
|
|
unowned var nextCourse: Course?
|
|
init(name: String, in department: Department) {
|
|
self.name = name
|
|
self.department = department
|
|
self.nextCourse = nil
|
|
}
|
|
}
|
|
```
|
|
-->
|
|
|
|
`Department` maintains a strong reference
|
|
to each course that the department offers.
|
|
In the ARC ownership model, a department owns its courses.
|
|
`Course` has two unowned references,
|
|
one to the department
|
|
and one to the next course a student should take;
|
|
a course doesn't own either of these objects.
|
|
Every course is part of some department
|
|
so the `department` property isn't an optional.
|
|
However,
|
|
because some courses don't have a recommended follow-on course,
|
|
the `nextCourse` property is an optional.
|
|
|
|
Here's an example of using these classes:
|
|
|
|
```swift
|
|
let department = Department(name: "Horticulture")
|
|
|
|
let intro = Course(name: "Survey of Plants", in: department)
|
|
let intermediate = Course(name: "Growing Common Herbs", in: department)
|
|
let advanced = Course(name: "Caring for Tropical Plants", in: department)
|
|
|
|
intro.nextCourse = intermediate
|
|
intermediate.nextCourse = advanced
|
|
department.courses = [intro, intermediate, advanced]
|
|
```
|
|
|
|
<!--
|
|
- test: `unowned-optional-references`
|
|
|
|
```swifttest
|
|
-> let department = Department(name: "Horticulture")
|
|
|
|
-> let intro = Course(name: "Survey of Plants", in: department)
|
|
-> let intermediate = Course(name: "Growing Common Herbs", in: department)
|
|
-> let advanced = Course(name: "Caring for Tropical Plants", in: department)
|
|
|
|
-> intro.nextCourse = intermediate
|
|
-> intermediate.nextCourse = advanced
|
|
-> department.courses = [intro, intermediate, advanced]
|
|
```
|
|
-->
|
|
|
|
The code above creates a department and its three courses.
|
|
The intro and intermediate courses both have a suggested next course
|
|
stored in their `nextCourse` property,
|
|
which maintains an unowned optional reference to
|
|
the course a student should take after completing this one.
|
|
|
|

|
|
|
|
An unowned optional reference doesn't keep a strong hold
|
|
on the instance of the class that it wraps,
|
|
and so it doesn't prevent ARC from deallocating the instance.
|
|
It behaves the same as an unowned reference does under ARC,
|
|
except that an unowned optional reference can be `nil`.
|
|
|
|
Like non-optional unowned references,
|
|
you're responsible for ensuring that `nextCourse`
|
|
always refers to a course that hasn't been deallocated.
|
|
In this case, for example,
|
|
when you delete a course from `department.courses`
|
|
you also need to remove any references to it
|
|
that other courses might have.
|
|
|
|
> Note: The underlying type of an optional value is `Optional`,
|
|
> which is an enumeration in the Swift standard library.
|
|
> However, optionals are an exception to the rule that
|
|
> value types can't be marked with `unowned`.
|
|
>
|
|
> The optional that wraps the class
|
|
> doesn't use reference counting,
|
|
> so you don't need to maintain a strong reference to the optional.
|
|
|
|
<!--
|
|
- test: `unowned-can-be-optional`
|
|
|
|
```swifttest
|
|
>> class C { var x = 100 }
|
|
>> class D {
|
|
>> unowned var a: C
|
|
>> unowned var b: C?
|
|
>> init(value: C) {
|
|
>> self.a = value
|
|
>> self.b = value
|
|
>> }
|
|
>> }
|
|
>> var c = C() as C?
|
|
>> let d = D(value: c! )
|
|
>> print(d.a.x, d.b?.x as Any)
|
|
<< 100 Optional(100)
|
|
|
|
>> c = nil
|
|
// Now that the C instance is deallocated, access to d.a is an error.
|
|
// We manually nil out d.b, which is safe because d.b is an Optional and the
|
|
// enum stays in memory regardless of ARC deallocating the C instance.
|
|
>> d.b = nil
|
|
>> print(d.b?.x as Any)
|
|
<< nil
|
|
```
|
|
-->
|
|
|
|
### Unowned References and Implicitly Unwrapped Optional Properties
|
|
|
|
The examples for weak and unowned references above
|
|
cover two of the more common scenarios
|
|
in which it's necessary to break a strong reference cycle.
|
|
|
|
The `Person` and `Apartment` example shows
|
|
a situation where two properties, both of which are allowed to be `nil`,
|
|
have the potential to cause a strong reference cycle.
|
|
This scenario is best resolved with a weak reference.
|
|
|
|
The `Customer` and `CreditCard` example
|
|
shows a situation where one property that's allowed to be `nil`
|
|
and another property that can't be `nil`
|
|
have the potential to cause a strong reference cycle.
|
|
This scenario is best resolved with an unowned reference.
|
|
|
|
However, there's a third scenario,
|
|
in which *both* properties should always have a value,
|
|
and neither property should ever be `nil` once initialization is complete.
|
|
In this scenario, it's useful to combine an unowned property on one class
|
|
with an implicitly unwrapped optional property on the other class.
|
|
|
|
This enables both properties to be accessed directly
|
|
(without optional unwrapping) once initialization is complete,
|
|
while still avoiding a reference cycle.
|
|
This section shows you how to set up such a relationship.
|
|
|
|
The example below defines two classes, `Country` and `City`,
|
|
each of which stores an instance of the other class as a property.
|
|
In this data model, every country must always have a capital city,
|
|
and every city must always belong to a country.
|
|
To represent this, the `Country` class has a `capitalCity` property,
|
|
and the `City` class has a `country` property:
|
|
|
|
```swift
|
|
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
|
|
}
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `implicitlyUnwrappedOptionals`
|
|
|
|
```swifttest
|
|
-> 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 set up the interdependency between the two classes,
|
|
the initializer for `City` takes a `Country` instance,
|
|
and stores this instance in its `country` property.
|
|
|
|
The initializer for `City` is called from within the initializer for `Country`.
|
|
However, the initializer for `Country` can't pass `self` to the `City` initializer
|
|
until a new `Country` instance is fully initialized,
|
|
as described in <doc:Initialization#Two-Phase-Initialization>.
|
|
|
|
To cope with this requirement,
|
|
you declare the `capitalCity` property of `Country` as
|
|
an implicitly unwrapped optional property,
|
|
indicated by the exclamation point at the end of its type annotation (`City!`).
|
|
This means that the `capitalCity` property has a default value of `nil`,
|
|
like any other optional,
|
|
but can be accessed without the need to unwrap its value
|
|
as described in <doc:TheBasics#Implicitly-Unwrapped-Optionals>.
|
|
|
|
Because `capitalCity` has a default `nil` value,
|
|
a new `Country` instance is considered fully initialized
|
|
as soon as the `Country` instance sets its `name` property within its initializer.
|
|
This means that the `Country` initializer can start to reference and pass around
|
|
the implicit `self` property as soon as the `name` property is set.
|
|
The `Country` initializer can therefore pass `self` as one of the parameters for
|
|
the `City` initializer when the `Country` initializer is setting
|
|
its own `capitalCity` property.
|
|
|
|
All of this means that you can create the `Country` and `City` instances
|
|
in a single statement, without creating a strong reference cycle,
|
|
and the `capitalCity` property can be accessed directly,
|
|
without needing to use an exclamation point to unwrap its optional value:
|
|
|
|
```swift
|
|
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".
|
|
```
|
|
|
|
<!--
|
|
- test: `implicitlyUnwrappedOptionals`
|
|
|
|
```swifttest
|
|
-> var country = Country(name: "Canada", capitalName: "Ottawa")
|
|
-> print("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
|
<- Canada's capital city is called Ottawa
|
|
```
|
|
-->
|
|
|
|
In the example above, the use of an implicitly unwrapped optional
|
|
means that all of the two-phase class initializer requirements are satisfied.
|
|
The `capitalCity` property can be used and accessed like a non-optional value
|
|
once initialization is complete,
|
|
while still avoiding a strong reference cycle.
|
|
|
|
## Strong Reference Cycles for Closures
|
|
|
|
You saw above how a strong reference cycle can be created
|
|
when two class instance properties hold a strong reference to 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 you assign a closure to a property of a class instance,
|
|
and the body of that closure captures the instance.
|
|
This capture might occur because the closure's body accesses a property of the instance,
|
|
such as `self.someProperty`,
|
|
or because the closure calls a method on the instance,
|
|
such as `self.someMethod()`.
|
|
In either case, these accesses cause the closure to “capture” `self`,
|
|
creating a strong reference cycle.
|
|
|
|
This strong reference cycle occurs because closures, like classes, are *reference types*.
|
|
When you assign a closure to a property,
|
|
you are assigning a *reference* to that closure.
|
|
In essence, it's the same problem as above ---
|
|
two strong references are keeping each other alive.
|
|
However, rather than two class instances,
|
|
this time it's a class instance and a closure that are keeping each other alive.
|
|
|
|
Swift provides an elegant solution to this problem,
|
|
known as a *closure capture list*.
|
|
However, before you learn how to break a strong reference cycle with a closure capture list,
|
|
it's useful to understand how such a cycle can be caused.
|
|
|
|
The example below shows how you can create a strong reference cycle
|
|
when using a closure that references `self`.
|
|
This example defines a class called `HTMLElement`,
|
|
which provides a simple model for an individual element within an HTML document:
|
|
|
|
```swift
|
|
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")
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
-> 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` property,
|
|
which 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,
|
|
which you can set to a string that represents
|
|
the text to be rendered within that HTML element.
|
|
|
|
In addition to these two simple properties,
|
|
the `HTMLElement` class defines a lazy property called `asHTML`.
|
|
This property references a closure that combines `name` and `text`
|
|
into an HTML string fragment.
|
|
The `asHTML` property is of type `() -> String`,
|
|
or “a function that takes no parameters, and returns a `String` value”.
|
|
|
|
By default, the `asHTML` property is assigned a closure that returns
|
|
a string representation of an HTML tag.
|
|
This tag contains the optional `text` value if it exists,
|
|
or no text content if `text` doesn't exist.
|
|
For a paragraph element, the closure would return `"<p>some text</p>"` or `"<p />"`,
|
|
depending on whether the `text` property equals `"some text"` or `nil`.
|
|
|
|
The `asHTML` property is named and used somewhat like an instance method.
|
|
However, because `asHTML` is a closure property rather than an instance method,
|
|
you can replace the default value of the `asHTML` property with a custom closure,
|
|
if you want to change the HTML rendering for a particular HTML element.
|
|
|
|
For example, the `asHTML` property could be set to a closure
|
|
that defaults to some text if the `text` property is `nil`,
|
|
in order to prevent the representation from returning an empty HTML tag:
|
|
|
|
```swift
|
|
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>".
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
-> let heading = HTMLElement(name: "h1")
|
|
-> let defaultText = "some default text"
|
|
-> heading.asHTML = {
|
|
return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
|
|
}
|
|
-> print(heading.asHTML())
|
|
<- <h1>some default text</h1>
|
|
```
|
|
-->
|
|
|
|
> Note: The `asHTML` property is declared as a lazy property,
|
|
> because it's only needed if and when 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`
|
|
> within the default closure,
|
|
> because the lazy property will not be accessed until
|
|
> after initialization has been completed and `self` is known to exist.
|
|
|
|
The `HTMLElement` class provides a single initializer,
|
|
which takes a `name` argument and (if desired) a `text` argument
|
|
to initialize a new element.
|
|
The class also defines a deinitializer,
|
|
which prints a message to show when an `HTMLElement` instance is deallocated.
|
|
|
|
Here's how you use the `HTMLElement` class to create and print a new instance:
|
|
|
|
```swift
|
|
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
|
print(paragraph!.asHTML())
|
|
// Prints "<p>hello, world</p>".
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
-> var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
|
-> print(paragraph!.asHTML())
|
|
<- <p>hello, world</p>
|
|
```
|
|
-->
|
|
|
|
> Note: The `paragraph` variable above is defined as an *optional* `HTMLElement`,
|
|
> so that it can be set to `nil` below to demonstrate
|
|
> the presence of a strong reference cycle.
|
|
|
|
Unfortunately, the `HTMLElement` class, as written above,
|
|
creates a strong reference cycle between
|
|
an `HTMLElement` instance and the closure used for its default `asHTML` value.
|
|
Here's how the cycle looks:
|
|
|
|

|
|
|
|
The instance's `asHTML` property holds a strong reference to its closure.
|
|
However, because the closure refers to `self` within its body
|
|
(as a way to reference `self.name` and `self.text`),
|
|
the closure *captures* self,
|
|
which means that it holds a strong reference back to the `HTMLElement` instance.
|
|
A strong reference cycle is created between the two.
|
|
(For more information about capturing values in a closure,
|
|
see <doc:Closures#Capturing-Values>.)
|
|
|
|
> Note: Even though the closure refers to `self` multiple times,
|
|
> it only captures one strong reference to the `HTMLElement` instance.
|
|
|
|
If you set the `paragraph` variable to `nil`
|
|
and break its strong reference to the `HTMLElement` instance,
|
|
the strong reference cycle prevents deallocating
|
|
both the `HTMLElement` instance and its closure:
|
|
|
|
```swift
|
|
paragraph = nil
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
-> paragraph = nil
|
|
```
|
|
-->
|
|
|
|
Note that the message in the `HTMLElement` deinitializer isn't printed,
|
|
which shows that the `HTMLElement` instance isn't deallocated.
|
|
|
|
## Resolving Strong Reference Cycles for Closures
|
|
|
|
You resolve a strong reference cycle between a closure and a class instance
|
|
by defining a *capture list* as part of the closure's definition.
|
|
A capture list defines the rules to use when capturing one or more reference types
|
|
within the closure's body.
|
|
As with strong reference cycles between two class instances,
|
|
you declare each captured reference to be a weak or unowned reference
|
|
rather than a strong reference.
|
|
The appropriate choice of weak or unowned depends on
|
|
the relationships between the different parts of your code.
|
|
|
|
> Note: Swift requires you to write `self.someProperty` or `self.someMethod()`
|
|
> (rather than just `someProperty` or `someMethod()`)
|
|
> whenever you refer to a member of `self` within a closure.
|
|
> This helps you remember that it's possible to capture `self` by accident.
|
|
|
|
### Defining a Capture List
|
|
|
|
Each item in a capture list is a pairing of the `weak` or `unowned` keyword
|
|
with a reference to a class instance (such as `self`)
|
|
or a variable initialized with some value (such as `delegate = self.delegate`).
|
|
These pairings are written within a pair of square braces, separated by commas.
|
|
|
|
Place the capture list before a closure's parameter list and return type
|
|
if they're provided:
|
|
|
|
```swift
|
|
lazy var someClosure = {
|
|
[unowned self, weak delegate = self.delegate]
|
|
(index: Int, stringToProcess: String) -> String in
|
|
// closure body goes here
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
>> class SomeClass {
|
|
>> var delegate: AnyObject?
|
|
lazy var someClosure = {
|
|
[unowned self, weak delegate = self.delegate]
|
|
(index: Int, stringToProcess: String) -> String in
|
|
// closure body goes here
|
|
>> return "foo"
|
|
}
|
|
>> }
|
|
```
|
|
-->
|
|
|
|
If a closure doesn't specify a parameter list or return type
|
|
because they can be inferred from context,
|
|
place the capture list at the very start of the closure,
|
|
followed by the `in` keyword:
|
|
|
|
```swift
|
|
lazy var someClosure = {
|
|
[unowned self, weak delegate = self.delegate] in
|
|
// closure body goes here
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `strongReferenceCyclesForClosures`
|
|
|
|
```swifttest
|
|
>> class AnotherClass {
|
|
>> var delegate: AnyObject?
|
|
lazy var someClosure = {
|
|
[unowned self, weak delegate = self.delegate] in
|
|
// closure body goes here
|
|
>> return "foo"
|
|
}
|
|
>> }
|
|
```
|
|
-->
|
|
|
|
### Weak and Unowned References
|
|
|
|
Define a capture in a closure as an unowned reference
|
|
when the closure and the instance it captures will always refer to each other,
|
|
and will always be deallocated at the same time.
|
|
|
|
Conversely, define a capture as a weak reference when the captured reference
|
|
may become `nil` at some point in the future.
|
|
Weak references are always of an optional type,
|
|
and automatically become `nil` when the instance they reference is deallocated.
|
|
This enables you to check for their existence within the closure's body.
|
|
|
|
<!--
|
|
<rdar://problem/28812110> Reframe discussion of weak/unowned closure capture in terms of object graph
|
|
-->
|
|
|
|
> Note: If the captured reference will never become `nil`,
|
|
> it should always be captured as an unowned reference,
|
|
> rather than a weak reference.
|
|
|
|
An unowned reference is the appropriate capture method to use to resolve
|
|
the strong reference cycle in the `HTMLElement` example
|
|
from <doc:AutomaticReferenceCounting#Strong-Reference-Cycles-for-Closures> above.
|
|
Here's how you write the `HTMLElement` class to avoid the cycle:
|
|
|
|
```swift
|
|
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")
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferencesForClosures`
|
|
|
|
```swifttest
|
|
-> 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")
|
|
}
|
|
|
|
}
|
|
```
|
|
-->
|
|
|
|
This implementation of `HTMLElement` is identical to the previous implementation,
|
|
apart from the addition of a capture list within the `asHTML` closure.
|
|
In this case, the capture list is `[unowned self]`,
|
|
which means “capture self as an unowned reference rather than a strong reference”.
|
|
|
|
You can create and print an `HTMLElement` instance as before:
|
|
|
|
```swift
|
|
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
|
print(paragraph!.asHTML())
|
|
// Prints "<p>hello, world</p>".
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferencesForClosures`
|
|
|
|
```swifttest
|
|
-> var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
|
-> print(paragraph!.asHTML())
|
|
<- <p>hello, world</p>
|
|
```
|
|
-->
|
|
|
|
Here's how the references look with the capture list in place:
|
|
|
|

|
|
|
|
This time, the capture of `self` by the closure is an unowned reference,
|
|
and doesn't keep a strong hold on the `HTMLElement` instance it has captured.
|
|
If you set the strong reference from the `paragraph` variable to `nil`,
|
|
the `HTMLElement` instance is deallocated,
|
|
as can be seen from the printing of its deinitializer message in the example below:
|
|
|
|
```swift
|
|
paragraph = nil
|
|
// Prints "p is being deinitialized".
|
|
```
|
|
|
|
<!--
|
|
- test: `unownedReferencesForClosures`
|
|
|
|
```swifttest
|
|
-> paragraph = nil
|
|
<- p is being deinitialized
|
|
```
|
|
-->
|
|
|
|
For more information about capture lists,
|
|
see <doc:Expressions#Capture-Lists>.
|
|
|
|
<!--
|
|
This source file is part of the Swift.org open source project
|
|
|
|
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
|
Licensed under Apache License v2.0 with Runtime Library Exception
|
|
|
|
See https://swift.org/LICENSE.txt for license information
|
|
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
-->
|