Search code examples
swiftpropertiesclosureslazy-loadingcircular-reference

Swift Lazy Property Initialization Circular Reference Compilation Error?


Can anyone explain why the following code will result in a circular reference error while compiling?

class Foo {
    
    let closure: ()->()
    
    func someFunc() {}
    
    init(closure: @escaping ()->()) {
        self.closure = closure
    }
}

class Bar {
    
    lazy var foo = Foo { [weak self] in // Circular Reference error here!
        self?.foo.someFunc()
    }
}

However if I change it to:

class Bar {

    lazy var foo: Foo = {
        return Foo { [weak self] in
            self?.foo.someFunc()
        }
    }()
}

then there is no circular reference error anymore.

I'm confused why one would cause an error, and the other would not.


Solution

  • Updated:

    The circular reference is in the type inferencing: It's the fact that foo's type depends on self.foo's type, which depends on foo's type, which depends on self.foo's type etc..

    Previous Answer:

    I think I figured out the root cause here (Thanks to some of my coworkers!):

    My coworker noticed that this code compiles fine, which points out it might be a type inference issue. I was being thrown off by the wrapping closure, which apparently has nothing to do with the real issue.

    class Bar {
        
        lazy var foo: Foo = Foo { [weak self] in
            self?.foo.someFunc()
        }
    }
    

    I think the in order for the compiler to evaluate Bar it must do type inference on the property foo, which requires the compiler to evaluate the property initialization code. However the property initialization code itself contains a reference to self/Bar, so in order for that code to be evaluated, Bar must be evaluated first. And thus the circular reference.