Apartment
выше будет верным для апартаментов иметь возможность не иметь постояльца в какой-то период их жизненного цикла, и так что слабая ссылка здесь является подходящим способом разбить цикл ссылок в данном случае. В противоположность, используйте невладеющие ссылки, когда другой объект имеет то же или большее время жизни.
weak
перед объявлением свойства или переменной.
nil
, когда объект, на который идёт ссылка, будет деаллоцирован. И так как слабым ссылкам нужно иметь возможность менять свои значения на nil
в процессе выполнения, то они всегда объявляются как переменные нежели константы опционального типа.
nil
.
Person
и Apartment
выше с одним большим отличием. В этот раз тип свойства tenant Apartment
объявлен как слабая ссылка:
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") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
Здесь представлено, как выглядят ссылки между двумя объектами вместе:
Person
всё ещё имеет сильную ссылку на объект Apartment
, но объект Apartment
теперь имеет слабую ссылку на объект Person
. Это означает, что когда Вы разобьете строгую ссылку, удерживаемую переменной john
, установкой в nil
, то на объект Person
больше не будет сильных ссылок:
john = nil
// Вывод "John Appleseed is being deinitialized"
Так как больше нет сильных ссылок на объект Person
, то он будет деалоцирован, а свойство tenant
установлено в nil
:
Apartment
идёт из переменной unit4A
. Если Вы разобьёт эту сильную ссылку, то не останется больше сильных сырок на объект Apartment
:
unit4A = nil
// Выведет "Apartment 4A is being deinitialized"
Так как больше нет сильных ссылок на объект Apartment
, то он так же будет деаллоцирован:
unowned
перед объявлением свойства или переменной.
nil
, что значит, что невладеющие ссылки определяются с использованием неопциональных типов.
Customer
и CreditCard
, которые моделируют пользователя банка и возможную кредитную карту для него. Эти два класса оба содержат объект другого в качестве свойства. Это отношение потенциально может создать сильный цикл ссылок.
Customer
и CreditCard
отличны от тех, что были между Apartment
и Person
, которые можно было увидеть в примере со слабой ссылкой выше. В этой модели данных клиент может иметь, а может не иметь кредитной карты, но кредитная карта всегда связана с клиентом. Объект CreditCard
никогда не переживает объект Customer
, на который ссылается. Для представления этого класс Customer
имеет опциональное свойство card
, но класс CreditCard
имеет невладеющее (и неопциональное) свойство customer
.
CreditCard
может быть создан только передачей значения number
и объекта Customer
в специальный инициализатор CreditCard
. Это гарантирует, что объект CreditCard
всегда будет иметь связанный с ним объект customer
, когда CreditCard
будет создан.
customer
в качестве невладеющего для избежания сильного цикла ссылок:
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") }
}
Свойство number класса CreditCard
определено с типом UInt64
вместо Int
для того, чтобы быть уверенными, что вместимость свойства number
достаточно объемно, чтобы хранить 16-значный номер карты и на 32-битной, и на 64-битной системах.
Customer?
с названием john
, которая будет использована для хранения ссылки на конкретного клиента. Эта переменная имеет начально значение nil
, что достигается её опциональностью:
var john: Customer?
Customer
и использовать его для инициализации и присваивания нового объекта CreditCard
его свойству card
:
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
Здесь представлено то, как выглядят эти ссылки:
Customer
теперь имеет сильную ссылку на объект CreditCard
, а объект CreditCard
теперь имеет невладеющую ссылку на объект Customer
.
customer
когда Вы разрушите сильную ссылку от переменной john
, то не останется больше сильных ссылок на объект Customer
:
Customer
, то он будет деаллоцирован. После того, как это произойдёт, то не будет больше сильных ссылок на объект CreditCard
, а значит он тоже будет деаллоцирован:
john = nil
// Выведет "John Appleseed is being deinitialized"
// Выведет "Card #1234567890123456 is being deinitialized"
Последний пример кода выше показывает, что деинициализаторы для объекта Customer
и CreditCard
оба выводят сообщения "deinitialized" после того, как переменная john
установлена в nil
.
unowned(unsafe)
. Если Вы попытаетесь получить доступ к небезопасной невладеющей ссылке после того, как объект, на который он ссылается, будет деаллоцирован, Ваша программа попытается получить доступ к локации памяти, где объект мог бы быть, что является небезопасной операцией.
Person
и Apartment
демонстрируют ситуацию, где два свойства, каждому из которых разрешено быть nil
, имеют потенциальную возможность создать цикл сильных ссылок. Этот сценарий лучше всего разрешается с помощью слабых ссылок.
Customer
и CreditCard
показывают ситуацию, когда одному свойству разрешено быть nil
, а другому это не позволено, и оба этих свойства имеют возможность создать цикл сильных ссылок. Такой сценарий лучше всего разрешается с помощью невладеющих ссылок.
nil
после завершения инициализации. В этом сценарии полезным будет объединить невладеющее свойство одного класса с неявно извлекаемым опциональным свойством у другого класса.
Country
и City
, каждый из которых хранит объект другого класса в качестве свойства. В этой модели данных каждая страна должна иметь столичный город, и каждый город должен принадлежать стране. Чтобы представить это, класс Country
имеет свойство capitalCity
, а класс City
имеет свойство country
:
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
}
}
Чтобы установить взаимную зависимость между двумя классами, инициализатор для City
принимает экземпляр Country
и сохраняет его в качестве свойства country
.
City
вызывается изнутри инициализатора Country
. Однако инициализатор Country
не может передать self
в инициализатор City
до тех пор, пока объект Country
не будет полностью проинициализирован, как это описано в {Двухфазовой инициализации}.
capitalCity
у Country
в качестве неявно извлекаемого опционального свойства, указываемого с помощью восклицательного знака в конце аннотации типа (City!)
. Это означает, что свойство capitalCity
имеет значение по-умолчанию nil
, как и любой другой опционал, но он может быть использован без необходимости распаковывать его значение.
capitalCity
имеет значение по-умолчанию nil
, то новый объект Country
будет полностью проинициализирован, как только объект Country
установить своё свойство name
изнутри инициализатора. Это означает, что инициализатор Country
может начать ссылаться и передавать неявное свойство self
, как только свойство name
будет задано. Затем инициализатор Country
может передать self
в качестве одного из параметров для инициализатора City
, когда инициализатор Country
установить своё собственное свойство capitalCity
.
Country
и City
одной инструкцией без создания цикла сильных ссылок, а свойство capitalCity
можно использовать напрямую без необходимости применения восклицательного знака для извлечения опционального значения:
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"
В примере выше использование неявно извлекаемого опционала означает, что все требования для двухфазных инициализаторов класса выполняются. Свойство capitalCity
может быть использовано как и неопциональное значение, когда инициализация завершится, хотя так же избегается, как и ранее, цикл сильных ссылок.