15

I have something that really puzzles me, specifically the following code triggers a compiler error "unresolved identifier self", and I am not sure why this is happening, as lazy means that at the time the property will be used, the class is already instantiated. Am I missing something?

Many thanks in advance.

Here is the code

class FirstClass {
    unowned var second: SecondClass

    init(second:SecondClass) {
        self.second = second
        print("First reporting for duty")
    }

    func aMethod() {
        print("First's method reporting for duty")
    }
}


class SecondClass {

    lazy var first = FirstClass(second: self)

    func aMethod() {
        first.aMethod()
    }
}
2
  • What is it exactly that you want to do? Call aMethod from FirstClass in your SecondClass? Commented Jun 30, 2016 at 9:18
  • No, just lazily instantiate it, ignore the methods Commented Jun 30, 2016 at 9:22

1 Answer 1

20

For some reason, a lazy property needs an explicit type annotation if its initial value refers to self. This is mentioned on the swift-evolution mailing list, however I cannot explain why that is necessary.

With

lazy var first: FirstClass = FirstClass(second: self)
//            ^^^^^^^^^^^^

your code compiles and runs as expected.

Here is another example which demonstrates that the problem occurs also with structs, i.e. it is unrelated to subclassing:

func foo(x: Int) -> Int { return x + 1 }

struct MyClass {
    let x = 1

    lazy var y = foo(0)            // No compiler error
    lazy var z1 = foo(self.x)      // error: use of unresolved identifier 'self'
    lazy var z2: Int = foo(self.x) // No compiler error
}

The initial value of y does not depend on self and does not need a type annotation. The initial values of z1/z2 depend on self, and it compiles only with an explicit type annotation.

Update: This has been fixed in Swift 4/Xcode 9 beta 3, lazy property initializers can now reference instance members without explicit self, and without explicit type annotation. (Thanks to @hamish for the update.)

Sign up to request clarification or add additional context in comments.

7 Comments

W.r.t. "some reason": self may be of type SecondClass or a subclass type of SecondClass. This means the return type of some method of self may differ at runtime depending on what self is in the class hierarchy. If we were to use the return value of such a class method to lazily instantiate first, then we couldn't know at compile time the type of first (due to overriden return type in subclass), hence the requirement for explicit type annotation of first. Now, this shouldn't be an issue when using self as an argument to an initializer, but possibly the case above generalizes.
(where by "return type of some method self may differ at runtime" I refer to the case where the subclass introduces a new same-name method with same method parameters but different return type; yielding an ambiguous method choice in case self is a subclass object and if the lazy variable were not to have been explicitly type annotated. It's weird, though, that the compiler can't infer type e.g. from a supplied explicit return type when using a closure to instantiate the lazy var, e.g. lazy var first = { () -> FirstClass in FirstClass(second: self) }()).
@dfri: Thanks for your response. However, the problem seems to be unrelated to subclassing, I have added an example.
Also mentioned here: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…, but I haven't found the rationale for it yet.
Thankfully, this quirk is fixed in Swift 4 (specifically the build that ships with Xcode 9 beta 3) :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.