Les réponses existantes ne racontent que la moitié de l' conveniencehistoire. L'autre moitié de l'histoire, la moitié qu'aucune des réponses existantes ne couvre, répond à la question que Desmond a postée dans les commentaires:
Pourquoi Swift me forcerait-il à mettre conveniencedevant mon initialiseur simplement parce que j'ai besoin d'appeler self.initdepuis celui-ci?
Je l'ai légèrement abordé dans cette réponse , dans laquelle je couvre en détail plusieurs règles d'initialisation de Swift, mais l'accent était principalement mis sur le requiredmot. Mais cette réponse concernait toujours quelque chose qui est pertinent pour cette question et cette réponse. Nous devons comprendre comment fonctionne l'héritage de l'initialisation Swift.
Étant donné que Swift n'autorise pas les variables non initialisées, vous n'êtes pas assuré d'hériter de tous les initialiseurs (ou de certains) de la classe dont vous héritez. Si nous sous-classons et ajoutons des variables d'instance non initialisées à notre sous-classe, nous avons arrêté d'hériter des initialiseurs. Et jusqu'à ce que nous ajoutions nos propres initialiseurs, le compilateur nous hurlera dessus.
Pour être clair, une variable d'instance non initialisée est toute variable d'instance qui n'a pas de valeur par défaut (en gardant à l'esprit que les options et les options implicitement déballées prennent automatiquement la valeur par défaut de nil).
Donc dans ce cas:
class Foo {
var a: Int
}
aest une variable d'instance non initialisée. Cela ne se compilera que si nous donnons aune valeur par défaut:
class Foo {
var a: Int = 0
}
ou initialisez adans une méthode d'initialisation:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Maintenant, voyons ce qui se passe si nous sous Foo- classons , d'accord?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
Droite? Nous avons ajouté une variable, et nous avons ajouté un initialiseur pour définir une valeur bafin qu'il compile. En fonction de la langue que vous venez, vous pourriez attendre à ce que Bara hérité Foode initialiseur de », init(a: Int). Mais ce n'est pas le cas. Et comment le pourrait-il? Comment Foode » init(a: Int)le savoir comment attribuer une valeur à la bvariable Barajoutée? Ce n'est pas le cas. Nous ne pouvons donc pas initialiser une Barinstance avec un initialiseur qui ne peut pas initialiser toutes nos valeurs.
Qu'est-ce que tout cela a à voir avec convenience?
Eh bien, regardons les règles sur l'héritage d'initialiseur :
Règle 1
Si votre sous-classe ne définit aucun initialiseur désigné, elle hérite automatiquement de tous ses initialiseurs désignés par superclasse.
Règle 2
Si votre sous-classe fournit une implémentation de tous ses initialiseurs désignés de superclasse - soit en les héritant selon la règle 1, soit en fournissant une implémentation personnalisée dans le cadre de sa définition - alors elle hérite automatiquement de tous les initialiseurs de commodité de superclasse.
Remarquez la règle 2, qui mentionne les initialiseurs de commodité.
Ainsi, ce que fait le conveniencemot - clé , c'est nous indiquer quels initialiseurs peuvent être hérités par des sous-classes qui ajoutent des variables d'instance sans valeurs par défaut.
Prenons cet exemple de Baseclasse:
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)
}
}
Notez que nous avons trois convenienceinitialiseurs ici. Cela signifie que nous avons trois initialiseurs qui peuvent être hérités. Et nous avons un initialiseur désigné (un initialiseur désigné est simplement n'importe quel initialiseur qui n'est pas un initialiseur pratique).
Nous pouvons instancier des instances de la classe de base de quatre manières différentes:

Alors, créons une sous-classe.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Nous héritons de Base. Nous avons ajouté notre propre variable d'instance et nous ne lui avons pas donné de valeur par défaut, nous devons donc ajouter nos propres initialiseurs. Nous avons ajouté un, init(a: Int, b: Int, c: Int)mais il ne correspond pas à la signature de la Baseclasse est désignée initialiseur: init(a: Int, b: Int). Cela signifie que nous n'héritons d' aucun initialiseur de Base:

Alors, que se passerait-il si nous héritions de Base, mais que nous allions de l'avant et implémentions un initialiseur qui correspondait à l'initialiseur désigné 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)
}
}
Maintenant, en plus des deux initialiseurs que nous avons implémentés directement dans cette classe, parce que nous avons implémenté l'initialiseur Basedésigné d' une classe correspondant à un initialiseur, nous pouvons hériter de tous Baseles convenienceinitialiseurs de classe :

Le fait que l'initialiseur avec la signature correspondante soit marqué comme conveniencene fait aucune différence ici. Cela signifie seulement qu'il Inheritorn'y a qu'un seul initialiseur désigné. Donc, si nous héritons de Inheritor, nous aurions juste à implémenter cet initialiseur désigné, puis nous hériterions Inheritorde l'initialiseur de commodité, ce qui signifie que nous avons implémenté tous Baseles initialiseurs désignés et que nous pouvons hériter de ses convenienceinitialiseurs.