Поскольку Swift поддерживает перегрузку методов и инициализаторов, вы можете поместить несколько init
элементов рядом друг с другом и использовать то, что сочтете удобным:
class Person {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name = "John"
}
}
Итак, почему convenience
ключевое слово вообще существует? Что делает следующее значительно лучше?
class Person {
var name:String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "John")
}
}
swift
initialization
Десмонд Хьюм
источник
источник
Ответы:
Существующие ответы рассказывают только половину
convenience
истории. Другая половина истории, та, которую не охватывает ни один из существующих ответов, отвечает на вопрос, который Десмонд разместил в комментариях:Я прикоснулся на нее чуть - чуть в этом ответе , в котором я охватывать несколько правил инициализатора Свифта в деталях, но основное внимание было на
required
слово. Но этот ответ все еще относился к тому, что имеет отношение к этому вопросу и этому ответу. Мы должны понять, как работает наследование инициализатора Swift.Поскольку Swift не допускает неинициализированных переменных, вам не гарантируется наследование всех (или любых) инициализаторов от класса, от которого вы наследуете. Если мы создадим подкласс и добавим к нашему подклассу любые неинициализированные переменные экземпляра, мы перестанем наследовать инициализаторы. И пока мы не добавим собственные инициализаторы, компилятор будет кричать на нас.
Чтобы быть ясным, неинициализированная переменная экземпляра - это любая переменная экземпляра, которой не присвоено значение по умолчанию (имея в виду, что опциональные параметры и неявно развернутые опции автоматически принимают значение по умолчанию, равное
nil
).Итак, в этом случае:
a
- неинициализированная переменная экземпляра. Это не будет компилироваться, если мы не укажемa
значение по умолчанию:или инициализировать
a
в методе инициализатора:Теперь давайте посмотрим, что произойдет, если мы создадим подкласс
Foo
, не так ли?Правильно? Мы добавили переменную и добавили инициализатор, чтобы установить значение,
b
чтобы оно скомпилировалось. В зависимости от того, с какого языка вы пришли, вы можете ожидать, чтоBar
унаследованныйFoo
инициализаторinit(a: Int)
. Но это не так. И как это могло быть? КакFoo
«sinit(a: Int)
знают , как присвоить значение кb
переменной , котораяBar
добавляется? Это не так. Таким образом, мы не можем инициализироватьBar
экземпляр с инициализатором, который не может инициализировать все наши значения.При чем здесь все это
convenience
?Что ж, давайте посмотрим на правила наследования инициализаторов :
Обратите внимание на Правило 2, в котором упоминаются удобные инициализаторы.
Так что
convenience
ключевое слово делает сделать , это указать нам , который Инициализаторы могут быть унаследованы подклассами, добавить переменный экземпляр без значений по умолчанию.Возьмем этот пример
Base
класса:Обратите внимание, что у нас
convenience
здесь три инициализатора. Это означает, что у нас есть три инициализатора, которые можно унаследовать. И у нас есть один назначенный инициализатор (назначенный инициализатор - это просто любой инициализатор, который не является вспомогательным инициализатором).Мы можем создать экземпляры базового класса четырьмя различными способами:
Итак, создадим подкласс.
Мы наследуем от
Base
. Мы добавили нашу собственную переменную экземпляра и не дали ей значения по умолчанию, поэтому мы должны добавить свои собственные инициализаторы. Мы добавили один,init(a: Int, b: Int, c: Int)
, но это не соответствует подписиBase
класса обозначается инициализатором:init(a: Int, b: Int)
. Это означает, что мы не наследуя никаких инициализаторами отBase
:Итак, что бы произошло, если бы мы унаследовали от
Base
, но мы пошли дальше и реализовали инициализатор, который соответствовал назначенному инициализатору отBase
?Теперь, в дополнение к двум инициализаторам, которые мы реализовали непосредственно в этом классе, поскольку мы реализовали инициализатор, соответствующий
Base
классу инициализатора, мы можем унаследовать все инициализаторыBase
классаconvenience
:Тот факт, что инициализатор с соответствующей подписью помечен как,
convenience
здесь не имеет значения. Это только означает, что уInheritor
него есть только один назначенный инициализатор. Итак, если мы наследуем отInheritor
, нам просто нужно было бы реализовать этот один назначенный инициализатор, а затем мы унаследовали быInheritor
удобный инициализатор, что, в свою очередь, означает, что мы реализовали всеBase
назначенные инициализаторы и можем наследовать егоconvenience
инициализаторы.источник
init(a: Int)
оставит неинициализированнымb
.В основном ясность. Из вашего второго примера,
требуется или обозначено . Он должен инициализировать все ваши константы и переменные. Инициализаторы для удобства не являются обязательными и обычно могут использоваться для облегчения инициализации. Например, предположим, что ваш класс Person имеет необязательную переменную пол:
где Gender - это перечисление
у вас могут быть удобные инициализаторы, подобные этому
Инициализаторы удобства должны вызывать в них назначенные или обязательные инициализаторы. Если ваш класс является подклассом, он должен вызывать
super.init()
внутри своей инициализации.источник
convenience
ключевого слова, но Swift все равно будет глючить по этому поводу. Это не та простота, которую я ожидал от Apple =)Что ж, первое, что мне приходит в голову, это то, что он используется в наследовании классов для организации кода и удобства чтения. Продолжая
Person
урок, подумайте о таком сценарииС помощью инициализатора удобства я могу создать
Employee()
объект без значения, отсюда и словоconvenience
источник
convenience
убрать ключевые слова, разве Swift не получит достаточно информации, чтобы вести себя точно так же?convenience
ключевое слово, вы не сможете инициализироватьEmployee
объект без аргументов.Employee()
вызываетconvenience
инициализатор (унаследованный из-за )init()
, который вызываетself.init(name: "Unknown")
.init(name: String)
, также удобный инициализатор дляEmployee
, вызывает назначенный инициализатор.Помимо пунктов, которые объяснили другие пользователи, здесь я немного понимаю.
Я прочно чувствую связь между инициализатором удобства и расширениями. На мой взгляд, удобные инициализаторы наиболее полезны, когда я хочу изменить (в большинстве случаев сделать это коротким или простым) инициализацию существующего класса.
Например, какой-то сторонний класс, который вы используете, имеет
init
четыре параметра, но в вашем приложении последние два имеют одинаковое значение. Чтобы избежать лишнего набора текста и сделать ваш код чистым, вы можете определить aconvenience init
только с двумя параметрами и внутри него вызыватьself.init
с последними параметрами со значениями по умолчанию.источник
convenience
перед инициализатором только потому, что мне нужно с него позвонитьself.init
? Это кажется лишним и неудобным.Согласно документации Swift 2.1 ,
convenience
инициализаторы должны придерживаться определенных правил:convenience
Инициализатор может вызвать только инициализатор в том же классе, а не в супер - классах (только по горизонтали, а не вверх)convenience
Инициализатор должен назвать назначенным инициализатором где - то в цепиconvenience
Инициализатор не может изменить ЛЮБОЕ свойство , прежде чем он назвал еще один инициализатор - тогда назначенный инициализатор должен инициализировать свойства, которые вводятся в текущем классе перед вызовом другого инициализатора.Используя
convenience
ключевое слово, компилятор Swift знает, что он должен проверить эти условия - в противном случае он не смог бы.источник
convenience
ключевого слова.let
свойства). Он не может инициализировать свойства. Назначенный инициализатор отвечает за инициализацию всех введенных свойств перед вызовомsuper
назначенного инициализатора.У класса может быть несколько назначенных инициализаторов. Удобный инициализатор - это вторичный инициализатор, который должен вызывать назначенный инициализатор того же класса.
источник