Retain cycle (circular reference) возникает, когда два или более объекта ссылаются друг на друга, не позволяя их деаллоцировать. В результате могут возникать утечки памяти, что приводит к тому, что ваше приложение потребляет больше памяти, чем необходимо.
Open
Наглядная иллюстрация приведенного ниже кода:
Имплементация retain cycle, когда объекты ссылаются друг на друга:
final class Person {
let firstName: String
let lastName: String
var parent: Person?
var kid: Person?
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
deinit {
print("Person \(firstName) deinit called")
}
}
var kidJohn: Person? = Person(firstName: "John", lastName: "Deere")
var parentSara: Person? = Person(firstName: "Sara", lastName: "Deere")
kidJohn?.parent = parentSara
parentSara?.kid = kidJohn
Установим экземпляры класса kidJohn
и parentSara
nil:
// deinit не вызовется
kidJohn = nil
parentSara = nil
Экземпляры (kidJohn
и parentSara
) не будут освобождены, нет сообщений «deinit», произойдет утечка памяти.
Почему это происходит? Для этого рассмотрим визуальный пример:
Использование так называемых weak (слабых) ссылок — это способ избежать retain cycle. Если вы объявляете ссылку слабой (weak)), то эта ссылка не препятствует освобождению экземпляра. Давайте изменим наш код и посмотрим, что произойдет:
weak var parent: Person?
weak var kid: Person?
Остаются только слабые ссылки, а экземпляры будут удалены.
Open
Если мы напишем следующий код:
final class Person {
let firstName: String
let lastName: String
// deinit вызовется
// тк fullNameVarFromClosure это не closure, это вычисляемое свойство
// которое возвращает String
// оно по дефолту @noescape и никаких capture list здесь не нужно
// [ссылка](https://michael-kiley.medium.com/why-your-lazy-vars-arent-creating-strong-reference-cycles-in-ios-d512ff2c9403)
lazy var fullNameVarFromClosure : String = {
return self.firstName + " " + self.lastName
}()
// deinit вызовется
var fullNameComputedVar: String {
self.firstName + " " + self.lastName
}
// deinit не вызовется
lazy var fullNameClosure: () -> String = {
return self.firstName + " " + self.lastName
}
// deinit не вызовется
lazy var closure: () -> Void = { [self] in
self.closure()
}
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
deinit {
print("Person \(firstName) deinit called")
}
}
var personJohn: Person? = Person(firstName: "John", lastName: "Deere")
// deinit вызовется
print(personJohn!.fullNameVarFromClosure)
personJohn = nil
// deinit не вызовется
print(personJohn!.fullNameClosure)
personJohn = nil
Несмотря на то, что внутри fullNameVar
мы ссылаем на самого себя deinit
будет вызван! Причина в том что fullNameVar
это value тип.
А fullNameClosure
- это closure, то есть reference тип.
lazy var fullNameClosure: () -> String = { [weak self] in
return self!.firstName + " " + self!.lastName
}
3.1.3.1.3 Autoreleasepool Theme | Back To iOSWiki Contents | 3.1.3.1.4.2 Strong Reference Theme