Автоматический подсчет ссылок
Swift использует Automatic Reference Counting (ARC), чтобы отследить и управлять использованием памяти Вашего приложения. В большинстве случаев это означает, что управление памятью “просто работает” в Swift, и Вы не должны думать об управлении памятью сами. Когда те экземпляры больше не необходимы, ARC автоматически высвобождает память, используемую экземплярами класса.
Однако в нескольких случаях ARC запрашивает больше информации об отношениях между частями Вашего кода для управления памятью для Вас. В этой главе описываются те ситуации и показывает, как Вы позволяете ARC управлять всей памятью своего приложения.
Как работы ARC
Каждый раз, когда Вы создаете новый экземпляр класса, ARC выделяет блок памяти, чтобы хранить информацию о том экземпляре. Эта память содержит информацию о типе экземпляра, вместе со значениями любых сохраненных свойств, связанных с тем экземпляром.
Кроме того, когда экземпляр больше не необходим, ARC высвобождает память, используемую тем экземпляром так, чтобы память могла использоваться для других целей вместо этого. Это гарантирует, чтобы экземпляры класса не занимали место в памяти, когда они больше не необходимы.
Однако, если ARC должен был освободить экземпляр, все еще использовавшийся, больше не будет возможно получить доступ к свойствам того экземпляра или вызвать методы того экземпляра. Действительно, если бы Вы попытались получить доступ к экземпляру, то Ваше приложение наиболее вероятно отказало бы.
Удостоверяться, что экземпляры не исчезают, в то время как они все еще необходимы, дорожки ARC, сколько свойств, констант и переменных в настоящее время относится к каждому экземпляру класса. ARC не освободит экземпляр, целая по крайней мере одна активная ссылка на тот экземпляр все еще существует.
Для создания этого возможным, каждый раз, когда Вы присваиваете экземпляр класса свойству, постоянному, или переменному, то свойство, постоянное, или переменное, делает сильную ссылку на экземпляр. Ссылку вызывают “сильной “ссылкой, потому что она сохраняет фирму, держатся, что экземпляр, и не позволяет ей быть освобожденной столько, сколько остается та сильная ссылка.
ARC в действии
Вот пример того, как работает Автоматический Подсчет ссылок. Этот пример запускается с простого вызванного класса Person
, который определяет сохраненное постоянное вызванное свойство name
:
class Person {
let name: String
init(name: String) {
self.name = name
println("\(name) is being initialized")
}
deinit {
println("\(name) is being deinitialized")
}
}
Person
класс имеет инициализатор, устанавливающий экземпляр name
свойство и печать сообщение, чтобы указать, что инициализация в стадии реализации. Person
класс также имеет deinitializer, распечатывающий сообщение, когда освобожден экземпляр класса.
Следующий фрагмент кода определяет три переменные типа Person?
, которые используются для установки многократных ссылок на новое Person
экземпляр в последующих фрагментах кода. Поскольку эти переменные имеют дополнительный тип (Person?
, нет Person
), они автоматически инициализируются со значением nil
, и в настоящее время не ссылайтесь на a Person
экземпляр.
var reference1: Person?
var reference2: Person?
var reference3: Person?
Можно теперь создать новое Person
экземпляр и присваивает его одной из этих трех переменных:
reference1 = Person(name: "John Appleseed")
// prints "John Appleseed is being initialized"
Обратите внимание на то, что сообщение "John Appleseed is being initialized"
распечатан в точке, которую Вы вызываете Person
инициализатор класса. Это подтверждает, что инициализация имела место.
Поскольку новое Person
экземпляр был присвоен reference1
переменная, существует теперь сильная ссылка от reference1
к новому Person
экземпляр. Поскольку существует по крайней мере одна сильная ссылка, ARC удостоверяется что это Person
сохранен в памяти и не освобожден.
Если Вы присваиваете то же Person
экземпляр к еще двум переменным, еще две сильных ссылки к тому экземпляру установлены:
reference2 = reference1
reference3 = reference1
Существует теперь три сильных ссылки к этому синглу Person
экземпляр.
Если Вы повреждаете две из этих сильных ссылок (включая исходную ссылку) путем присвоения nil
к двум из переменных единственная сильная ссылка остается, и Person
экземпляр не освобожден:
reference1 = nil
reference2 = nil
ARC не освобождает Person
экземпляр до третьей и заключительной сильной ссылки повреждается, в которой точке ясно, что Вы больше не используете Person
экземпляр:
reference3 = nil
// prints "John Appleseed is being deinitialized"
Циклы сильной ссылки между экземплярами класса
В примерах выше, ARC в состоянии отследить число ссылок на новое Person
экземпляр Вы создаете и освобождать это Person
экземпляр, когда это больше не необходимо.
Однако возможно записать код, в котором экземпляр класса никогда не добирается до точки, где это имеет нулевые сильные ссылки. Это может произойти, если два экземпляра класса содержат сильную ссылку друг другу, такому, что каждый экземпляр поддерживает другой. Это известно как цикл сильной ссылки.
Вы разрешаете циклы сильной ссылки путем определения некоторых отношений между классами как слабые или ненаходящиеся в собственности ссылки вместо как сильные ссылки. Этот процесс описан в Разрешении Циклов Сильной ссылки Между Экземплярами класса. Однако перед изучением, как разрешить цикл сильной ссылки, полезно понять, как вызывается такой цикл.
Вот пример того, как цикл сильной ссылки может быть создан случайно. Этот пример определяет два вызванные класса Person
и Apartment
, которые моделируют блок квартир и его резидентных объектов:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}
Каждый Person
экземпляр имеет a name
свойство типа String
и дополнительное apartment
свойство, которое является первоначально nil
. apartment
свойство является дополнительным, потому что у лица может не всегда быть квартиры.
Точно так же каждый Apartment
экземпляр имеет a number
свойство типа Int
и имеет дополнительное tenant
свойство, которое является первоначально nil
. Свойство арендатора является дополнительным, потому что квартира может не всегда иметь арендатора.
Оба из этих классов также определяют deinitializer, распечатывающий факт, что экземпляр того класса является deinitialized. Это позволяет Вам видеть ли экземпляры Person
и Apartment
освобождаются как ожидалось.
Этот следующий фрагмент кода определяет две переменные дополнительного вызванного типа john
и number73
, который будет установлен в определенное Apartment
и Person
экземпляр ниже. Обе из этих переменных имеют начальное значение nil
, на основании того, чтобы быть дополнительным:
var john: Person?
var number73: Apartment?
Можно теперь создать определенное Person
экземпляр и Apartment
экземпляр и присваивает эти новые экземпляры john
и number73
переменные:
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
Вот то, как сильные ссылки заботятся о создании и присвоении этих двух экземпляров. john
переменная теперь имеет сильную ссылку к новому Person
экземпляр, и number73
переменная имеет сильную ссылку к новому Apartment
экземпляр:
Можно теперь соединить два экземпляра так, чтобы у лица была квартира, и квартира имеет арендатора. Обратите внимание на то, что восклицательный знак (!
) используется, чтобы развернуть и получить доступ к экземплярам, сохраненным в john
и number73
дополнительные переменные, так, чтобы могли быть установлены свойства тех экземпляров:
john!.apartment = number73
number73!.tenant = john
Вот то, как сильные ссылки заботятся о Вас, соединяют эти два экземпляра:
К сожалению, соединение этих двух экземпляров создает цикл сильной ссылки между ними. Person
экземпляр теперь имеет сильную ссылку к Apartment
экземпляр, и Apartment
экземпляр имеет сильную ссылку к Person
экземпляр. Поэтому, когда Вы повреждаете сильные ссылки, сохраненные john
и number73
переменные, подсчеты ссылок не бросать! для обнуления, и экземпляры не освобождены ARC:
john = nil
number73 = nil
Обратите внимание на то, что ни один, deinitializer вызвали при установке этих двух переменных в nil
. Цикл сильной ссылки предотвращает Person
и Apartment
экземпляры от того, чтобы когда-нибудь быть освобожденным, вызывая утечку памяти в Вашем приложении.
Вот то, как сильные ссылки заботятся о Вас, устанавливает john
и number73
переменные к nil
:
Сильные ссылки между Person
экземпляр и Apartment
экземпляр остается и не может быть поврежден.
Разрешение циклов сильной ссылки между экземплярами класса
Когда Вы работаете со свойствами типа класса, Swift обеспечивает два способа разрешить циклы сильной ссылки: слабые ссылки и ненаходящиеся в собственности ссылки.
Слабые и ненаходящиеся в собственности ссылки позволяют одному экземпляру в ссылочном цикле относиться к другому экземпляру, не сохраняя сильное, держат его. Экземпляры могут тогда относиться друг к другу, не создавая цикл сильной ссылки.
Используйте слабую ссылку каждый раз, когда это допустимо для той ссылки для становления nil
в некоторый момент во время его времени жизни. С другой стороны используйте ненаходящуюся в собственности ссылку, когда Вы знаете, что ссылка никогда не будет nil
как только это было установлено во время инициализации.
Слабые ссылки
Слабая ссылка является ссылкой, не сохраняющей сильное, держат экземпляр, который она отсылает к, и так не мешает ARC избавиться от экземпляра, на который ссылаются. Это поведение препятствует тому, чтобы ссылка стала частью цикла сильной ссылки. Вы указываете слабую ссылку путем размещения weak
ключевое слово перед свойством или объявлением переменной.
Используйте слабую ссылку для предотвращения ссылочных циклов каждый раз, когда для той ссылки возможно не иметь “никакого значения” в некоторый момент в его жизни. Если ссылка будет всегда иметь значение, используйте ненаходящуюся в собственности ссылку вместо этого, как описано в Ненаходящихся в собственности Ссылках. В Apartment
пример выше, для квартиры подходяще быть в состоянии не иметь “никакого арендатора” в некоторый момент в ее времени жизни, и таким образом, слабая ссылка является надлежащим способом повредить ссылочный цикл в этом случае.
Поскольку слабым ссылкам позволяют не иметь “никакого значения”, необходимо объявить каждую слабую ссылку как наличие дополнительного типа. Дополнительные типы являются предпочтительным способом представлять возможность для “никакого значения” в Swift.
Поскольку слабая ссылка не сохраняет сильное, держат экземпляр, к которому она относится, для того экземпляра возможно быть освобожденным, в то время как слабая ссылка все еще относится к ней. Поэтому ARC автоматически устанавливает слабую ссылку на nil
когда освобожден экземпляр, к которому это относится. Можно проверить на существование значения в слабой ссылке, точно так же, как любое другое дополнительное значение, и Вы никогда не будете заканчивать со ссылкой на больше не существующий недопустимый экземпляр.
Пример ниже идентичен Person
и Apartment
пример сверху, с одним важным различием. На сей раз вокруг, Apartment
тип tenant
свойство объявляется как слабая ссылка:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}
Сильные ссылки от этих двух переменных (john
и number73
) и ссылки между этими двумя экземплярами создаются как прежде:
var john: Person?
var number73: Apartment?
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
john!.apartment = number73
number73!.tenant = john
Вот то, как ссылки смотрят теперь, когда Вы соединили эти два экземпляра:
Person
экземпляр все еще имеет сильную ссылку к Apartment
экземпляр, но Apartment
экземпляр теперь имеет слабую ссылку на Person
экземпляр. Это означает это при повреждении сильной ссылки, сохраненной john
переменные, больше нет сильных ссылок к Person
экземпляр:
Поскольку больше нет сильных ссылок к Person
экземпляр, это освобождено:
john = nil
// prints "John Appleseed is being deinitialized"
Единственная остающаяся сильная ссылка к Apartment
экземпляр от number73
переменная. При повреждении той сильной ссылки больше нет сильных ссылок к Apartment
экземпляр:
Поскольку больше нет сильных ссылок к Apartment
экземпляр, это также освобождено:
number73 = nil
// prints "Apartment #73 is being deinitialized"
Заключительные два фрагмента кода выше шоу, что deinitializers для Person
экземпляр и Apartment
печать экземпляра их сообщения «deinitialized» после john
и number73
переменные установлены в nil
. Это доказывает, что был поврежден ссылочный цикл.
Ненаходящиеся в собственности ссылки
Как слабые ссылки, ненаходящаяся в собственности ссылка не сохраняет сильное, держат экземпляр, к которому она относится. В отличие от слабой ссылки, однако, ненаходящаяся в собственности ссылка, как предполагается, всегда имеет значение. Из-за этого ненаходящаяся в собственности ссылка всегда определяется как обязательный тип. Вы указываете ненаходящуюся в собственности ссылку путем размещения unowned
ключевое слово перед свойством или объявлением переменной.
Поскольку ненаходящаяся в собственности ссылка является обязательной, Вы не должны разворачивать ненаходящуюся в собственности ссылку каждый раз, когда это используется. К ненаходящейся в собственности ссылке можно всегда получать доступ непосредственно. Однако ARC не может установить ссылку на nil
когда экземпляр, к которому это относится, освобожден, потому что переменные обязательного типа не могут быть установлены в nil
.
Следующий пример определяет два класса, Customer
и CreditCard
, которые моделируют клиента банка и возможную кредитную карту для того клиента. Эти два класса каждое хранилище экземпляр другого класса как свойство. Это отношение имеет потенциал для создания цикла сильной ссылки.
Отношение между Customer
и CreditCard
немного отличается от отношения между Apartment
и Person
замеченный в примере слабой ссылки выше. В этой модели данных клиент может или может не иметь кредитной карты, но кредитная карта будет всегда связываться с клиентом. Представлять это, Customer
класс имеет дополнительное card
свойство, но CreditCard
класс имеет обязательное customer
свойство.
Кроме того, новое CreditCard
экземпляр может только быть создан путем передачи a number
значение и a customer
экземпляр к пользовательскому CreditCard
инициализатор. Это гарантирует этому a CreditCard
экземпляр всегда имеет a customer
экземпляр связался с ним когда CreditCard
экземпляр создается.
Поскольку кредитная карта будет всегда иметь клиента, Вы определяете customer
свойство как ненаходящаяся в собственности ссылка, для предотвращения цикла сильной ссылки:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { println("\(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 { println("Card #\(number) is being deinitialized") }
}
Этот следующий фрагмент кода определяет дополнительное Customer
переменную вызывают john
, который будет использоваться для хранения ссылки на определенного клиента. Эта переменная имеет начальное значение ноля, на основании того, чтобы быть дополнительным:
var john: Customer?
Можно теперь создать a 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
// prints "John Appleseed is being deinitialized"
// prints "Card #1234567890123456 is being deinitialized"
Заключительный фрагмент кода выше шоу, что deinitializers для Customer
экземпляр и CreditCard
экземпляр оба распечатывает их сообщения «deinitialized» после john
переменная установлена в nil
.
Ненаходящиеся в собственности ссылки и неявно развернутые дополнительные свойства
Примеры для слабых и ненаходящихся в собственности ссылок выше покрывают два из более общих сценариев, в которых необходимо повредить цикл сильной ссылки.
Person
и Apartment
пример показывает ситуацию, где два свойства, обоим из которых позволяют быть nil
, имейте потенциал для порождения цикла сильной ссылки. Этот сценарий лучше всего разрешен со слабой ссылкой.
Customer
и CreditCard
пример показывает ситуацию, где одно свойство, которому позволяют быть nil
и другое свойство, которое не может быть nil
имейте потенциал для порождения цикла сильной ссылки. Этот сценарий лучше всего разрешен с ненаходящейся в собственности ссылкой.
Однако существует третий сценарий, в котором оба свойства должны всегда иметь значение, и никакое свойство никогда не должно быть nil
как только инициализация завершена. В этом сценарии полезно объединить ненаходящееся в собственности свойство на одном классе с неявно развернутым дополнительным свойством на другом классе.
Это позволяет обоим свойствам быть полученными доступ непосредственно (без дополнительного разворачивания), как только инициализация завершена, все еще избегая ссылочного цикла. Этот раздел показывает Вам, как установить такое отношение.
Пример ниже определяет два класса, Country
и City
, каждый из которых хранит экземпляр другого класса как свойство. В этой модели данных каждая страна должна всегда иметь столицу, и каждый город должен всегда принадлежать стране. Представлять это, Country
класс имеет a capitalCity
свойство, и City
класс имеет a country
свойство:
class Country {
let name: String
let capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
Устанавливать взаимозависимость между этими двумя классами, инициализатором для City
берет a Country
экземпляр и хранилища этот экземпляр в country
свойство.
Инициализатор для City
вызывается из инициализатора для Country
. Однако инициализатор для Country
не может передать self
к City
инициализатор до нового Country
экземпляр полностью инициализируется, как описано в Двухфазной Инициализации.
Для разрешения с этим требованием Вы объявляете capitalCity
свойство Country
как неявно развернутое дополнительное свойство, обозначенное восклицательным знаком в конце его аннотации типа (City!
). Это означает что capitalCity
свойство имеет значение по умолчанию nil
, как любой другой дополнительный, но может быть получен доступ без потребности развернуть ее значение, как описано в Неявно Развернутом Optionals.
Поскольку capitalCity
имеет значение по умолчанию nil
значение, новое Country
экземпляр считают полностью инициализированным как только Country
экземпляр устанавливает name
свойство в его инициализаторе. Это означает что Country
инициализатор может начать ссылаться и раздавать неявное self
свойство, как только name
свойство установлено. Country
инициализатор может поэтому передать self
как один из параметров для City
инициализатор, когда Country
инициализатор устанавливает свое собственное capitalCity
свойство.
Все это означает, что можно создать Country
и City
экземпляры в отдельном операторе, не создавая цикл сильной ссылки, и capitalCity
к свойству можно получить доступ непосредственно, не будучи должен использовать восклицательный знак для разворачивания его дополнительного значения:
var country = Country(name: "Canada", capitalName: "Ottawa")
println("\(country.name)'s capital city is called \(country.capitalCity.name)")
// prints "Canada's capital city is called Ottawa"
В примере выше, использование неявно развернутого дополнительный означает, что удовлетворены все двухфазные требования инициализатора класса. capitalCity
свойство может использоваться и получаться доступ как обязательное значение, как только инициализация завершена, все еще избегая цикла сильной ссылки.
Циклы сильной ссылки для закрытий
Вы видели выше, как цикл сильной ссылки может быть создан, когда два свойства экземпляра класса содержат сильную ссылку друг другу. Вы также видели, как использовать слабые и ненаходящиеся в собственности ссылки для повреждения этих циклов сильной ссылки.
Цикл сильной ссылки может также произойти, если Вы присваиваете закрытие свойству экземпляра класса, и организация того закрытия получает экземпляр. Это получение могло бы произойти, потому что организация закрытия получает доступ к свойству экземпляра, такой как self.someProperty
, или потому что закрытие вызывает метод на экземпляре, такой как self.someMethod()
. В любом случае эти доступы заставляют закрытие «получать» self
, создание цикла сильной ссылки.
Этот цикл сильной ссылки происходит, потому что закрытия, как классы, являются ссылочными типами. При присвоении закрытия свойству Вы присваиваете ссылку на то закрытие. В сущности это - та же проблема как выше — две сильных ссылки поддерживают друг друга. Однако, а не два экземпляра класса, на сей раз это - экземпляр класса и закрытие, поддерживающие друг друга.
Swift предоставляет изящное решение этой проблемы, известной как список получения закрытия. Однако перед изучением, как повредить цикл сильной ссылки со списком получения закрытия, полезно понять, как может быть вызван такой цикл.
Пример ниже шоу, как можно создать цикл сильной ссылки при использовании закрытия, на которое это ссылается self
. Этот пример определяет вызванный класс HTMLElement
, который обеспечивает простую модель для отдельного элемента в документе HTML:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
HTMLElement
класс определяет a name
свойство, указывающее имя элемента, такой как "p"
для элемента абзаца, или "br"
для элемента разрыва строки. HTMLElement
также определяет дополнительное text
свойство, которое можно установить в строку, представляющую текст, который будет представлен в том элементе HTML.
В дополнение к этим двум простым свойствам, HTMLElement
класс определяет ленивое вызванное свойство asHTML
. Это свойство ссылается на объединяющееся закрытие name
и text
в HTML представляют фрагмент в виде строки. asHTML
свойство имеет тип () -> String
, или “функция, не берущая параметров и возвращающая a String
значение”.
По умолчанию, asHTML
свойство присваивается закрытие, возвращающее строковое представление HTML-тэга. Этот тег содержит дополнительное text
оцените, если это существует, или никакое текстовое содержание если text
не существует. Для элемента абзаца возвратилось бы закрытие "<p>some text</p>"
или "<p />"
, В зависимости от ли text
свойство равняется "some text"
или nil
.
asHTML
свойство называют и используют несколько как метод экземпляра. Однако, потому что asHTML
свойство закрытия, а не метод экземпляра, можно заменить значение по умолчанию asHTML
свойство с пользовательским закрытием, если Вы хотите изменить рендеринг HTML для определенного элемента HTML.
HTMLElement
класс обеспечивает единственный инициализатор, берущий a name
параметр и (при желании) a text
параметр для инициализации нового элемента. Класс также определяет deinitializer, распечатывающий сообщение для показа когда HTMLElement
экземпляр освобожден.
Вот то, как Вы используете HTMLElement
класс, чтобы создать и распечатать новый экземпляр:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
К сожалению, HTMLElement
класс, как записано выше, создает цикл сильной ссылки между HTMLElement
экземпляр и закрытие используются для его значения по умолчанию asHTML
значение. Вот то, как смотрит цикл:
Экземпляр asHTML
свойство содержит сильную ссылку к своему закрытию. Однако, потому что закрытие относится к self
в его организации (как способ сослаться self.name
и self.text
), закрытие получает сам, что означает, что оно сдерживает сильную ссылку к HTMLElement
экземпляр. Цикл сильной ссылки создается между двумя. (Для получения дополнительной информации о получении значений в закрытии, посмотрите Значения Получения.)
Если Вы устанавливаете paragraph
переменная к nil
и повредите его сильную ссылку к HTMLElement
экземпляр, ни один HTMLElement
экземпляр, ни его закрытие освобождены из-за цикла сильной ссылки:
paragraph = nil
Обратите внимание на то, что сообщение в HTMLElement
deinitializer не распечатан, который показывает что HTMLElement
экземпляр не освобожден.
Разрешение циклов сильной ссылки для закрытий
Вы разрешаете цикл сильной ссылки между закрытием и экземпляром класса путем определения списка получения как части определения закрытия. Список получения определяет правила использовать при получении один или несколько ссылочные типы в организации закрытия. Как с циклами сильной ссылки между двумя экземплярами класса, Вы объявляете, что каждая полученная ссылка слабая или ненаходящаяся в собственности ссылка, а не сильная ссылка. Надлежащий выбор слабых или ненаходящихся в собственности зависит от отношений между различными частями Вашего кода.
Определение списка получения
Каждый элемент в списке получения является соединением weak
или unowned
ключевое слово со ссылкой на экземпляр класса (такой как self
) или переменная, инициализированная с некоторым значением (такой как delegate = self.delegate!
). Эти соединения записаны в паре квадратных скобок, разделенных запятыми.
Поместите список получения перед списком параметров закрытия и типом возврата, если им предоставлены:
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
}
Слабые и ненаходящиеся в собственности ссылки
Определите получение в закрытии как ненаходящаяся в собственности ссылка, когда закрытие и экземпляр, который это получает, будут всегда относиться друг к другу и будут всегда освобождаться одновременно.
С другой стороны определите получение как слабую ссылку, когда полученная ссылка сможет стать nil
в некоторый момент в будущем. Слабые ссылки всегда имеют дополнительный тип, и автоматически становятся nil
. Когда экземпляр, на который они ссылаются освобожден, это позволяет Вам проверить на их существование в организации закрытия.
Ненаходящаяся в собственности ссылка является надлежащим методом получения для использования для разрешения цикла сильной ссылки в 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 {
println("\(name) is being deinitialized")
}
}
Эта реализация HTMLElement
идентично предыдущей реализации, кроме добавления списка получения в asHTML
закрытие. В этом случае список получения [unowned self]
, что означает “получение сам как ненаходящаяся в собственности ссылка, а не сильная ссылка”.
Можно создать и распечатать HTMLElement
экземпляр как прежде:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
Вот то, как ссылки смотрят со списком получения на месте:
На сей раз, получение self
закрытием ненаходящаяся в собственности ссылка и не сохраняет сильное, держатся HTMLElement
экземпляр это получило. Если Вы устанавливаете сильную ссылку от paragraph
переменная к nil
, HTMLElement
экземпляр освобожден, как видно из печати его сообщения deinitializer в примере ниже:
paragraph = nil
// prints "p is being deinitialized"