В языке Swift, чтобы инициализировать экземпляр, нужно заполнить все поля этого класса и только потом вызывать суперконструктор:
class Base {
var name: String
init(name: String) {
self.name = name
}
}
class Derived: Base {
var number: Int
init(name: String, number: Int) {
// won't compile if interchange lines
self.number = number
super.init(name)
}
}
Для меня это кажется задом наперед, потому что экземпляр должен self
быть создан до присвоения значений его полям, и этот код создает впечатление, будто цепочка происходит только после присваивания. Кроме того, суперкласс не имеет законных средств для чтения введенных атрибутов своего подкласса, поэтому безопасность в данном случае не имеет значения.
Кроме того, многие другие языки, такие как JavaScript и даже Objective C, который является в некоторой степени духовным предком Swift, требуют цепного вызова до доступа self
, а не после.
Что является причиной такого выбора, требующего определения полей перед вызовом суперконструктора?
self
.Ответы:
В C ++, когда вы создаете производный объект, он начинается как базовый объект во время работы конструктора Base, поэтому во время работы конструктора Base члены Derived даже не существуют. Поэтому их не нужно инициализировать, и их невозможно инициализировать. Только после завершения конструктора Base объект изменяется на производный объект с множеством неинициализированных полей, которые вы затем инициализируете.
В Swift, когда вы создаете производный объект, он является производным объектом с самого начала. Если методы переопределены, то метод Base init уже будет использовать переопределенные методы, которые могут обращаться к переменным производного члена. Следовательно, все производные переменные-члены должны быть инициализированы до вызова метода Base init.
PS. Вы упомянули Objective-C. В Objective-C все автоматически инициализируется равным 0 / nil / NO. Но если это значение не является правильным значением для инициализации переменной, тогда метод Base init может легко вызвать переопределенный метод и использовать еще не инициализированную переменную со значением 0 вместо правильного значения. В Objective-C это не нарушение правил языка (именно так оно определено для работы), а, очевидно, ошибка в вашем коде. В Swift эта ошибка не допускается языком.
PS. Есть комментарий "это производный объект с самого начала или его нельзя наблюдать из-за языковых правил"? Класс Derived инициализировал свои собственные члены до вызова метода Base init, и эти производные члены сохраняют свои значения. Так как он является производным объектом во время инициализации базы называется, или компилятор должен был бы сделать что - то довольно странно. И сразу после того, как метод инициализации Base инициализировал все члены экземпляра Base, он может вызвать переопределенные функции, и это докажет, что он является экземпляром производного класса.
источник
Это происходит из правил безопасности Swift, как описано в разделе « Двухфазная инициализация» на странице « Инициализация языкового документа» .
Это гарантирует, что каждое поле установлено перед использованием (особенно указатели, чтобы избежать сбоев).
Swift достигает этого с помощью двухфазной последовательности инициализации: каждый инициализатор должен инициализировать все свои поля экземпляра, а затем вызвать инициализатор суперкласса, чтобы сделать то же самое, и только после того, как это произойдет в дереве, эти инициализаторы могут позволить
self
указателю убежать, вызвать экземпляр методы, или прочитайте значения свойств экземпляра.Затем они могут выполнить дальнейшую инициализацию при условии, что объект хорошо сформирован. В частности, все необязательные указатели будут иметь допустимые значения. ноль не подходит для них.
Цель C не сильно отличается, за исключением того, что 0 или nil всегда является допустимым значением, поэтому инициализация первой фазы выполняется распределителем, просто устанавливая все поля в 0. Кроме того, Swift имеет неизменяемые поля, поэтому они должны инициализироваться в первой фазе. , И Swift обеспечивает соблюдение этих правил безопасности.
источник
Рассматривать
Поэтому не существует простого проекта, обеспечивающего безопасность подрядчика, когда разрешены виртуальные методы , Swift избегает этих проблем, требуя двухэтапной инициализации, что обеспечивает повышенную безопасность для программиста, а также приводит к более сложному языку.
Если вы можете решить эти проблемы хорошим способом, не пропустите «Go», перейдите непосредственно к коллекции вашего доктора философии…
источник