У меня есть два класса, Shape
иSquare
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
При реализации выше я получаю ошибку:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Почему я должен установить self.sideLength
перед звонком super.init
?
properties
compiler-errors
swift
JuJoDi
источник
источник
Ответы:
Цитата из языка программирования Swift, который отвечает на ваш вопрос:
источник
init
.Swift имеет очень четкую, специфическую последовательность операций, выполняемых в инициализаторах. Давайте начнем с некоторых основных примеров и перейдем к общему случаю.
Давайте возьмем объект А. Мы определим его следующим образом.
Обратите внимание, что A не имеет суперкласса, поэтому он не может вызвать функцию super.init (), поскольку он не существует.
Хорошо, теперь давайте подкласс A с новым классом с именем B.
Это отход от Objective-C,
[super init]
который обычно вызывается первым, прежде чем что-либо еще. Не так в Swift. Вы несете ответственность за обеспечение того, чтобы переменные вашего экземпляра находились в согласованном состоянии, прежде чем делать что-либо еще, включая вызов методов (включая инициализатор вашего суперкласса).источник
Из документов
Зачем нам нужна проверка безопасности, как это?
Чтобы ответить на этот вопрос, давайте перейдем к процессу инициализации в Swift.
Итак, чтобы убедиться, что двухэтапный процесс инициализации выполнен так, как определено выше, есть четыре проверки безопасности, одна из которых,
Теперь, двухфазная инициализация никогда не говорит о порядке, но эта проверка безопасности, вводит
super.init
в после инициализации всех свойств.Проверка безопасности 1 может показаться неуместной, поскольку двухфазная инициализация предотвращает доступ к значениям свойств до того, как они будут выполнены, без этой проверки безопасности 1.
Как в этом примере
Triangle.init
инициализировал каждое свойство перед использованием. Так что проверка безопасности 1 кажется неактуальной,Но тогда может быть другой сценарий, немного сложнее,
Вывод :
Здесь, если бы мы вызвали
super.init
перед установкойhypotenuse
, тоsuper.init
вызов вызвал быprintShapeDescription()
и, так как это было переопределено, он сначала вернулся бы к реализации класса TriangleprintShapeDescription()
. КлассprintShapeDescription()
Triangle обращается кhypotenuse
необязательному свойству, которое еще не было инициализировано. И это запрещено двухфазная инициализация предотвращает доступ к значениям свойств до их инициализации.Поэтому убедитесь, что двухфазная инициализация выполняется в соответствии с определением, должен быть определенный порядок вызовов
super.init
, то есть после инициализации всех свойств, введенныхself
классом, нам нужна проверка безопасности 1источник
init
могут вызывать (переопределенную) функцию ... в которой эта функция обращается к свойству подклассов, а затем, чтобы не задавать значения, вызовsuper
должен произойти после того, как все значения установлены. ОК имеет смысл. Интересно, как Objective-C сделал это тогда, и почему вы должны былиsuper
сначала позвонить ?printShapeDescription()
доself.sides = sides; self.name = named;
того, который будет генерировать эту ошибку:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. Ошибка OP дана для уменьшения «возможности» ошибки времени выполнения.printShapeDescription
была функция, которая не имела отношения к,self
т. Е. Это было что-то вроде «печать (« ничего »)», тогда не было бы проблем. (Тем не менее, даже для этого компилятор выдаст ошибку, потому что он не очень умный)«Super.init ()» следует вызывать после инициализации всех переменных вашего экземпляра.
В видео Apple «Intermediate Swift» (вы можете найти его на странице видеоматериала Apple Developer https://developer.apple.com/videos/wwdc/2014/ ) около 28:40 явно говорится, что все инициализаторы в Суперкласс должен называться ПОСЛЕ ТОГО, КАК вы инициализируете переменные вашего экземпляра.
В Objective-C все было наоборот. В Swift, поскольку все свойства должны быть инициализированы перед его использованием, нам нужно сначала инициализировать свойства. Это предназначено для предотвращения вызова переопределенной функции из метода init () суперкласса без предварительной инициализации свойств.
Так что реализация «Квадрата» должна быть:
источник
Извините за уродливое форматирование. Просто поставьте знак вопроса после объявления, и все будет хорошо. Вопрос говорит компилятору, что значение необязательно.
Edit1:
Есть лучший способ пропустить эту ошибку. Согласно комментарию jmaschad, в вашем случае нет причин использовать опциональные опции, потому что опциональные опции не удобны в использовании, и вы всегда должны проверять, имеет ли опциональное значение не ноль, прежде чем получить к нему доступ. Поэтому все, что вам нужно сделать, это инициализировать элемент после объявления:
Edit2:
После получения двух минусов на этот ответ я нашел еще лучший способ. Если вы хотите, чтобы член класса был инициализирован в вашем конструкторе, вы должны присвоить ему начальное значение внутри contructor и перед вызовом super.init (). Как это:
Удачи в изучении Swift.
источник
super.init(name:name)
иself.sideLength = sideLength
. ОбъявлениеsideLength
как необязательное вводит в заблуждение и создает дополнительные хлопоты позже, когда вам придется принудительно развернуть его.var sideLength: Double
, не нужно присваивать ему начальное значениеswift заставляет вас инициализировать каждый элемент var до того, как он когда-либо будет использоваться. Поскольку он не может быть уверен, что произойдет, когда наступит супер-поворот, он выдаст ошибку: лучше, чем потом сожалеть
источник
super.init
в первой строке?Эдвард,
Вы можете изменить код в вашем примере следующим образом:
Это использует неявно развернутый необязательный.
В документации мы можем прочитать:
источник
Swift не позволит вам инициализировать суперкласс без инициализации свойств, в противоположность Obj C. Поэтому вы должны инициализировать все свойства перед вызовом «super.init».
Пожалуйста, перейдите на http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Это дает хорошее объяснение вашей проблемы.
источник
Добавьте ноль в конец объявления.
Это сработало для моего случая, но может не сработать для вашего
источник
Вы просто начинаете в неправильном порядке.
источник
Я, вероятно, получу некоторые отрицательные отзывы, но, честно говоря, так легче жить:
источник
Это потрясающе глупый дизайн.
Рассмотрим что-то вроде этого:
Это неверно, как отмечено выше. Но это так:
Потому что «я» не было инициализировано.
Я искренне надеюсь, что эта ошибка будет исправлена в ближайшее время.
(Да, я знаю, что мог бы создать пустой объект и затем установить размер, но это просто глупо).
источник