Быстрое ленивое создание экземпляров с использованием себя

У меня есть кое-что, что меня действительно озадачивает, в частности, следующий код вызывает ошибку компилятора «неразрешенный идентификатор себя», и я не уверен, почему это происходит, поскольку ленивый означает, что в то время, когда свойство будет использоваться, экземпляр класса уже создан . Я что-то упускаю?

Спасибо заранее.

Вот код

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()
    }
}

person Razvan Soneriu    schedule 30.06.2016    source источник
comment
Что именно вы хотите сделать? Позвонить aMethod из FirstClass в свой SecondClass?   -  person Dershowitz123    schedule 30.06.2016
comment
Нет, просто лениво создайте экземпляр, игнорируя методы   -  person Razvan Soneriu    schedule 30.06.2016


Ответы (1)


По какой-то причине ленивому свойству требуется явная аннотация типа, если его начальное значение ссылается на self. Это упоминается в списке рассылки swift-evolution, однако я не могу объяснить, зачем это необходимо.

С

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

ваш код компилируется и работает, как и ожидалось.

Вот еще один пример, демонстрирующий, что проблема возникает и с structs, т.е. она не связана с созданием подклассов:

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
}

Начальное значение y не зависит от self и не нуждается в аннотации типа. Начальные значения z1/z2 зависят от self, и он компилируется только с явной аннотацией типа.

Обновление: это исправлено в Swift 4/Xcode 9. бета 3, ленивые инициализаторы свойств теперь могут ссылаться на члены экземпляра без явного self и без явной аннотации типа. (Спасибо @hamish за обновление.)

person Martin R    schedule 30.06.2016
comment
В.р.т. по какой-то причине: self может иметь тип SecondClass или тип подкласса SecondClass. Это означает, что возвращаемый тип некоторых методов self может отличаться во время выполнения в зависимости от того, что self находится в иерархии классов. Если бы мы использовали возвращаемое значение такого метода класса для ленивого создания экземпляра first, то мы не могли бы знать во время компиляции тип first (из-за переопределения возвращаемого типа в подклассе), следовательно, требуется явная аннотация типа first. . Теперь это не должно быть проблемой при использовании self в качестве аргумента для инициализатора, но, возможно, случай выше обобщает. - person dfrib; 30.06.2016
comment
(где под типом возвращаемого значения некоторого метода self может отличаться во время выполнения я имею в виду случай, когда подкласс вводит новый метод с тем же именем с теми же параметрами метода, но с другим типом возвращаемого значения, что приводит к неоднозначному выбору метода в случай self является объектом подкласса, и если ленивая переменная не должна была быть явно аннотирована по типу, Странно, однако, что компилятор не может вывести тип, например, из предоставленного явного возвращаемого типа при использовании замыкания для создания экземпляра ленивой переменной, например lazy var first = { () -> FirstClass in FirstClass(second: self) }()). - person dfrib; 30.06.2016
comment
@dfri: Спасибо за ваш ответ. Однако проблема, похоже, не связана с подклассами, я добавил пример. - person Martin R; 30.06.2016
comment
Также упоминается здесь: lists.swift.org/ pipermail/swift-evolution/Week-of-Mon-20160125/, но я пока не нашел для этого обоснования. - person Martin R; 30.06.2016
comment
Я вижу, тайна остается неразгаданной. Между тем, я думаю, это классифицируется как странность вывода типов. - person dfrib; 30.06.2016
comment
(W.r.t. ветка swift-evo: интересно, что разработчик Apple Джо Грофф упоминает, что, насколько ему известно, ленивые переменные не могут ссылаться на self (поэтому, возможно, аннотация типа — это не совсем полная функция) ) - person dfrib; 30.06.2016
comment
К счастью, эта особенность исправлена ​​в Swift 4 (особенно в сборке, которая поставляется с Xcode 9). бета 3) :) - person Hamish; 11.07.2017