В этом случае вам не нужен список захвата, так как ссылка self
не принадлежит после создания экземпляра personalizedGreeting
.
Как пишет MartinR в своем комментарии, вы можете легко проверить свою гипотезу, зарегистрировав, был ли объект Person
деинициализирован или нет, когда вы удаляете список захвата.
E.g.
class Person {
var name: String
lazy var personalizedGreeting: String = {
_ in
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
deinit { print("deinitialized!") }
}
func foo() {
let p = Person(name: "Foo")
print(p.personalizedGreeting) // Hello Foo!
}
foo() // deinitialized!
Очевидно, что в этом случае нет риска сильного цикла ссылок и, следовательно, нет необходимости в списке захвата unowned self
в ленивом закрытии. Причина этого в том, что ленивое замыкание выполняется только один раз и использует только возвращаемое значение замыкания для (ленивого) создания экземпляра personalizedGreeting
, тогда как ссылка на self
в этом случае не переживает выполнение замыкания.
Однако если бы мы сохранили подобное замыкание в свойстве класса Person
, мы бы создали цикл строгой ссылки, поскольку свойство self
сохранило бы сильную ссылку обратно на self
. Например.:
class Person {
var name: String
var personalizedGreeting: (() -> String)?
init(name: String) {
self.name = name
personalizedGreeting = {
() -> String in return "Hello, \(self.name)!"
}
}
deinit { print("deinitialized!") }
}
func foo() {
let p = Person(name: "Foo")
}
foo() // ... nothing : strong reference cycle
Гипотеза: ленивое создание экземпляров замыканий автоматически фиксирует self
как weak
(или unowned
) по умолчанию.
Рассматривая следующий пример, мы понимаем, что эта гипотеза неверна.
/* Test 1: execute lazy instantiation closure */
class Bar {
var foo: Foo? = nil
}
class Foo {
let bar = Bar()
lazy var dummy: String = {
_ in
print("executed")
self.bar.foo = self
/* if self is captured as strong, the deinit
will never be reached, given that this
closure is executed */
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let f = Foo()
// Test 1: execute closure
print(f.dummy) // executed, dummy
}
foo() // ... nothing: strong reference cycle
То есть, f
в foo()
не деинициализируется, и, учитывая этот цикл строгой ссылки, мы можем сделать вывод, что self
сильно захвачено при создании экземпляра закрытия ленивой переменной dummy
.
Мы также можем видеть, что мы никогда не создаем цикл строгой ссылки в случае, если мы никогда не создаем экземпляр dummy
, который будет поддерживать то, что ленивое создание экземпляра не более одного раза можно рассматривать как область времени выполнения (во многом похожую на никогда не достигаемое если), то есть либо а) никогда не достигался (не инициализировался), либо б) достигался, полностью выполнялся и "выбрасывался" (конец области действия).
/* Test 2: don't execute lazy instantiation closure */
class Bar {
var foo: Foo? = nil
}
class Foo {
let bar = Bar()
lazy var dummy: String = {
_ in
print("executed")
self.bar.foo = self
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let p = Foo()
// Test 2: don't execute closure
// print(p.dummy)
}
foo() // deinitialized!
Дополнительную информацию о циклах с сильными ссылками см., например, в
person
dfrib
schedule
01.07.2016
deinit
и проверьте, вызывается ли он, когда вы ожидаете, что объект будет освобожден. Или используйте инструменты отладки памяти в Xcode/Instruments. - person Martin R   schedule 01.07.2016lazy
инициализаторами. - person holex   schedule 01.07.2016