iosswiftlazy-evaluation

Why does Swift's lazy var solve the error: Cannot assign value of type '(HomeViewController) -> () -> HomeViewController' to type


Below code gives an error:

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    let tableView = {
        let t = UITableView(frame: .zero, style: .grouped)
        t.dataSource = self
        t.delegate = self
        return t
    }()

    ...
}

Error is:

Cannot assign value of type '(HomeViewController) -> () -> HomeViewController' to type '(any UITableViewDataSource)?'

However, if I change it to lazy var tableView = {}(), then error goes away:

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    lazy var tableView = {
        let t = UITableView(frame: .zero, style: .grouped)
        t.dataSource = self
        t.delegate = self
        return t
    }()

    ...

}

Why does lazy var solve this error?


Solution

  • self cannot be used in property initialisers because they are run before self is fully initialised. The error message produced by this code makes it clear:

    class Foo {
        let x = 1
        let y = x + 1 // Cannot use instance member 'x' within property initializer; property initializers run before 'self' is available
    }
    

    On the other hand, the initialisers of lazy vars are guaranteed to be run after self has been fully initialised, so you can use self there.

    With self being unavailable, the token self resolves to the NSObject method named self. This is an instance method that takes no arguments and returns Self, so normally it would be of type () -> HomeViewController. But in this case, there is no instance to call this instance method on, and you end up accessing the curried form. This is of type (HomeViewController) -> () -> HomeViewController - i.e. a method that takes an instance of HomeViewController, and returns a function that does what the self method would do.

    // f is of type (HomeViewController) -> () -> HomeViewController
    let f = HomeViewController.`self`
    
    // another example:
    // g is of type (String) -> () -> String
    let g = String.lowercased
    g("ABC") // this expression is of type () -> String
    g("ABC")() // this is of type String, and evaluates to "abc"