Существующие ответы рассказывают только половину convenienceистории. Другая половина истории, та, которую не охватывает ни один из существующих ответов, отвечает на вопрос, который Десмонд разместил в комментариях:
Зачем Swift заставляет меня ставить convenienceперед инициализатором только потому, что мне нужно с него позвонить self.init?
Я прикоснулся на нее чуть - чуть в этом ответе , в котором я охватывать несколько правил инициализатора Свифта в деталях, но основное внимание было на requiredслово. Но этот ответ все еще относился к тому, что имеет отношение к этому вопросу и этому ответу. Мы должны понять, как работает наследование инициализатора Swift.
Поскольку Swift не допускает неинициализированных переменных, вам не гарантируется наследование всех (или любых) инициализаторов от класса, от которого вы наследуете. Если мы создадим подкласс и добавим к нашему подклассу любые неинициализированные переменные экземпляра, мы перестанем наследовать инициализаторы. И пока мы не добавим собственные инициализаторы, компилятор будет кричать на нас.
Чтобы быть ясным, неинициализированная переменная экземпляра - это любая переменная экземпляра, которой не присвоено значение по умолчанию (имея в виду, что опциональные параметры и неявно развернутые опции автоматически принимают значение по умолчанию, равное nil).
Итак, в этом случае:
class Foo {
var a: Int
}
a- неинициализированная переменная экземпляра. Это не будет компилироваться, если мы не укажем aзначение по умолчанию:
class Foo {
var a: Int = 0
}
или инициализировать aв методе инициализатора:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Теперь давайте посмотрим, что произойдет, если мы создадим подкласс Foo, не так ли?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
Правильно? Мы добавили переменную и добавили инициализатор, чтобы установить значение, bчтобы оно скомпилировалось. В зависимости от того, с какого языка вы пришли, вы можете ожидать, что Barунаследованный Fooинициализатор init(a: Int). Но это не так. И как это могло быть? Как Foo«s init(a: Int)знают , как присвоить значение к bпеременной , которая Barдобавляется? Это не так. Таким образом, мы не можем инициализировать Barэкземпляр с инициализатором, который не может инициализировать все наши значения.
При чем здесь все это convenience?
Что ж, давайте посмотрим на правила наследования инициализаторов :
Правило 1
Если ваш подкласс не определяет назначенные инициализаторы, он автоматически наследует все назначенные инициализаторы суперкласса.
Правило 2
Если ваш подкласс предоставляет реализацию всех назначенных ему инициализаторов суперкласса - либо наследуя их в соответствии с правилом 1, либо предоставляя настраиваемую реализацию как часть своего определения - тогда он автоматически наследует все вспомогательные инициализаторы суперкласса.
Обратите внимание на Правило 2, в котором упоминаются удобные инициализаторы.
Так что convenienceключевое слово делает сделать , это указать нам , который Инициализаторы могут быть унаследованы подклассами, добавить переменный экземпляр без значений по умолчанию.
Возьмем этот пример Baseкласса:
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
Обратите внимание, что у нас convenienceздесь три инициализатора. Это означает, что у нас есть три инициализатора, которые можно унаследовать. И у нас есть один назначенный инициализатор (назначенный инициализатор - это просто любой инициализатор, который не является вспомогательным инициализатором).
Мы можем создать экземпляры базового класса четырьмя различными способами:

Итак, создадим подкласс.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Мы наследуем от Base. Мы добавили нашу собственную переменную экземпляра и не дали ей значения по умолчанию, поэтому мы должны добавить свои собственные инициализаторы. Мы добавили один, init(a: Int, b: Int, c: Int), но это не соответствует подписи Baseкласса обозначается инициализатором: init(a: Int, b: Int). Это означает, что мы не наследуя никаких инициализаторами от Base:

Итак, что бы произошло, если бы мы унаследовали от Base, но мы пошли дальше и реализовали инициализатор, который соответствовал назначенному инициализатору от Base?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
Теперь, в дополнение к двум инициализаторам, которые мы реализовали непосредственно в этом классе, поскольку мы реализовали инициализатор, соответствующий Baseклассу инициализатора, мы можем унаследовать все инициализаторы Baseкласса convenience:

Тот факт, что инициализатор с соответствующей подписью помечен как, convenienceздесь не имеет значения. Это только означает, что у Inheritorнего есть только один назначенный инициализатор. Итак, если мы наследуем от Inheritor, нам просто нужно было бы реализовать этот один назначенный инициализатор, а затем мы унаследовали бы Inheritorудобный инициализатор, что, в свою очередь, означает, что мы реализовали все Baseназначенные инициализаторы и можем наследовать его convenienceинициализаторы.