Lazy property and didSet causes infinite recursive calls

bug
(Yilei He) #1
import UIKit

class ViewController: UIViewController {

    var s: MyStruct! {
        didSet {
            print(n)
        }
    }

    lazy var n: Int = s.n + 1

    override func viewDidLoad() {
        super.viewDidLoad()
        s = MyStruct()
    }
}

struct MyStruct {
    lazy var n = 1
    init() {}
}

==============================================================
When running above code, it will cause infinite recursive call. The calling track starting from viewDidLoad looks like this:
set var s => didSet => initialise lazy var n(in ViewController) => initialise lazy var n(in MyStruct) => didSet => initialise lazy var n(in ViewController) => initialise lazy var n(in MyStruct) => didSet …

I can understand that accessing a lazy property on a value type property triggers "didSet". But I couldn't understand why lazy initialiser is called multiple times. Is this expected behaviour or a bug in swift?

#2

I don't know the internals, but it looks like didSet is being invoked before the logic that marks n as initialized. This causes the read to (re)invoke the lazy initializer. This seems like a tough call. I would expect willSet to definitely behave this way, since that is invoked before setting the property. Whether didSet should work, or is intended to work, the same way are good questions.

2 Likes