3

Why do we get strong reference circle in the second example, why we did not in the first example?

class Test1 {
    var name: String = "Ted"
    lazy var greeting  = { return "Hello \(self.name)" }()
    deinit{print("goodby1")} // No retain cycle here ..
}
var v1:Test1? = Test1()
v1?.greeting
v1 = nil //prints goodby1, dealocation done

class Test {
    var name: String = "Mery"
    lazy var greeting = {return "Hello \(self.name)"}
    deinit{print("goodby")} //retain cycle here 

}
var v: Test? = Test()
v!.greeting
v = nil 
Nalov
  • 578
  • 5
  • 9

1 Answers1

3

In the first example, the closure is executed one time, it returns a String, and that String is assigned greeting. There is no closure; nothing is captured. This is just an anonymous function. If you like, you can get rid of the self., just like in a named function. The key point is that the type of greeting is String.

In the second example, greeting is a closure value, which captures self. Since self holds greeting and greeting holds self, there's a loop. The key point is that the type of greeting is () -> String. This is almost certainly a mistake, since this is not how lazy is intended to be used.

(lazy is a pretty weird feature in Swift, and was probably a bad idea to include in the langauge. I try to avoid it as much as possible. There are several subtle ways to use it incorrectly, so it's not surprising that it's bitten you.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • If I understand it right, if I use `[unowned self] in` and then use `self.name` in closure, then `self` should be deinitialized? – Robert Dresler Feb 05 '19 at 19:46
  • 1
    @RobertDresler That should be fine, since `unowned` does not hold a strong reference to its object. Make sure only to use it when you know the variable captured (self, in this case) will never be nil when the closure gets called. Otherwise, use `weak` and check if it has a value before using it. Something like `guard let strongSelf = self else { /* bail */ }`. – kid_x Feb 05 '19 at 20:52