быстрый ленивый вар с броском инициализации - PullRequest
0 голосов
/ 04 июня 2018

Я не уверен, что это ошибка или она действительно должна работать?

class A {
    init() throws { }
}

class B {
    lazy var instance = A()
}

этот код компилируется без ошибок, используя XCode 9 и последнюю версию Swift, и отлично работает, если только Class A init() действительно выбрасывает, тогда ленивая переменная имеет нулевой указатель.Но не должен ли этот код как-то не скомпилироваться?

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Это действительно ошибка ( SR-7862 ) - вы не можете выбросить ошибки из контекста инициализатора свойства (и даже если бы вы могли, вам потребовалось бы префикс вызова с try)поэтому компилятор должен выдавать ошибку.

Я открыл запрос на удаление, чтобы исправить это ( # 17022 ).

Редактировать: Теперь патч был добавлен в ветку 4.2, поэтому он будет исправлен для выпуска Swift 4.2 с Xcode 10 (и до релиза вы можете попробовать снимок 4.2 ).

0 голосов
/ 04 июня 2018

Как ответ на ваш вопрос:

Но не должен ли этот код каким-то образом не быть скомпилирован?

Ну, в какой-то момент ваш фрагмент кода работал безлюбая проблема (потому что, как вы упомянули, класс A init фактически не генерирует), поэтому он может быть скомпилирован без каких-либо проблем.Чтобы сделать это более понятным, рассмотрим случай, подобный следующему:

let myString: String? = nil
print(myString!) // crashes!

он будет скомпилирован просто отлично!хотя мы все знаем, что он вылетает при оценке myString!, то есть мы знаем, что он вызывает время выполнения сбой, но это не означает, что компилятор должен предотвращатьэто потому, что он может быть действительным в некоторый момент (например, если мы объявим его как let myString: String? = "Hello");Как и в вашем случае, он может быть действительным в некоторый момент - как упоминалось выше -.

Обычно для таких случаев мы, как разработчики, несем ответственность за его обработку в зависимости от желаемого поведения.


Обращаясь к этому случаю, мы могли бы спросить:

"Как мы можем реализовать переменную instance lazy, чтобы перехватить ошибку (с блоком do-catch)?"

На самом деле, этот код не будет компилироваться:

class B {
    lazy var instance:A = {
        do {
            let myA = try A()
            return myA
        } catch {
            print(error)
        }
    }()
}

с жалобой на то, что:

Отсутствует возврат в закрытии, ожидаемом длявернуть 'A'

, поскольку очевидно, что достижение блока catch означает, что возвращать нечего.Кроме того, как вы упомянули, даже если вы реализовали его как

lazy var instance = A()

, вы не получите ошибку времени компиляции, однако * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1045* попытка использования с фактическим броском должна привести ко времени выполненияошибка:

let myB = B()
print(myB.instance) // crash!

Что бы я предложил для решения этой проблемы, это объявить instance как ленивый необязательный переменная:

class B {
    lazy var instance:A? = {
        do {
            let myA = try A()
            return myA
        } catch {
            print(error)
        }

        return nil
    }()
}

На этом этапе, еслимы предполагаем, что A инициализатор всегда выдает, пытаясь получить к нему доступ:

let myB = B()
print(myB.instance)

должен регистрировать:

пойманная ошибка

ноль

без сбоев.В противном случае он должен работать нормально, например:

let myB = B()
myB.instance?.doSomething() // works fine
...