Le risposte esistenti raccontano solo metà della conveniencestoria. L'altra metà della storia, la metà che nessuna delle risposte esistenti copre, risponde alla domanda che Desmond ha pubblicato nei commenti:
Perché Swift mi costringerebbe a mettere conveniencedavanti al mio inizializzatore solo perché ho bisogno di chiamarlo self.init? `
L'ho toccato leggermente in questa risposta , in cui tratterò dettagliatamente alcune delle regole di inizializzazione di Swift, ma il focus principale era sulla requiredparola. Ma quella risposta stava ancora affrontando qualcosa che è rilevante per questa domanda e questa risposta. Dobbiamo capire come funziona l'ereditarietà dell'inizializzatore Swift.
Poiché Swift non consente variabili non inizializzate, non è garantito che erediti tutti (o alcuno) gli inizializzatori dalla classe da cui erediti. Se eseguiamo la sottoclasse e aggiungiamo variabili di istanza non inizializzate alla nostra sottoclasse, abbiamo smesso di ereditare gli inizializzatori. E finché non aggiungiamo i nostri inizializzatori, il compilatore ci urlerà contro.
Per essere chiari, una variabile di istanza non inizializzata è una qualsiasi variabile di istanza a cui non viene assegnato un valore predefinito (tenendo presente che gli opzionali e quelli implicitamente non scartati assumono automaticamente un valore predefinito di nil).
Quindi in questo caso:
class Foo {
var a: Int
}
aè una variabile di istanza non inizializzata. Questo non verrà compilato a meno che non diamo aun valore predefinito:
class Foo {
var a: Int = 0
}
o inizializza acon un metodo di inizializzazione:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Ora, vediamo cosa succede se effettuiamo la sottoclasse Foo, vero?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
Destra? Abbiamo aggiunto una variabile e aggiunto un inizializzatore per impostare un valore in bmodo che venga compilato. A seconda di quale lingua si sta venendo da, ci si potrebbe aspettare che Barha ereditato Foo's inizializzatore, init(a: Int). Ma non lo fa. E come potrebbe? Come fa Foo's init(a: Int)know how per assegnare un valore alla bvariabile che Barha aggiunto? Non Quindi non possiamo inizializzare aBar un'istanza con un inizializzatore che non può inizializzare tutti i nostri valori.
Cosa c'entra tutto questo convenience ?
Bene, diamo un'occhiata alle regole sull'ereditarietà dell'inizializzatore :
Regola 1
Se la sottoclasse non definisce alcun inizializzatore designato, eredita automaticamente tutti i suoi inizializzatori designati superclasse.
Regola 2
Se la sottoclasse fornisce un'implementazione di tutti i suoi inizializzatori designati della superclasse, ereditandoli secondo la regola 1 o fornendo un'implementazione personalizzata come parte della sua definizione, eredita automaticamente tutti gli inizializzatori di convenienza della superclasse.
Nota la Regola 2, che menziona gli inizializzatori di convenienza.
Così che la convenienceparola chiave non indicare a noi che inizializzatori possono essere ereditate dalle sottoclassi che le variabili di istanza aggiuntivo senza valori di default.
Prendiamo questa Baseclasse di esempio :
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)
}
}
Si noti che ne abbiamo tre convenience inizializzatori qui. Ciò significa che abbiamo tre inizializzatori che possono essere ereditati. E abbiamo un inizializzatore designato (un inizializzatore designato è semplicemente un inizializzatore che non è un inizializzatore di convenienza).
Possiamo istanziare istanze della classe base in quattro modi diversi:

Quindi, creiamo una sottoclasse.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Stiamo ereditando da Base. Abbiamo aggiunto la nostra variabile di istanza e non gli abbiamo dato un valore predefinito, quindi dobbiamo aggiungere i nostri inizializzatori. Abbiamo aggiunto uno, init(a: Int, b: Int, c: Int)ma non corrisponde alla firma del Basecodice categoria è designato inizializzatore: init(a: Int, b: Int). Ciò significa che non stiamo ereditando alcun inizializzatore da Base:

Quindi, cosa accadrebbe se ereditassimo Base, ma andassimo avanti e implementassimo un inizializzatore che corrispondeva all'inizializzatore designato 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)
}
}
Ora, oltre ai due inizializzatori che abbiamo implementato direttamente in questa classe, poiché abbiamo implementato un inizializzatore che Baseidentifica l'inizializzatore designato della classe, possiamo ereditare tutti Basegli convenienceinizializzatori della classe :

Il fatto che l'inizializzatore con la firma corrispondente sia contrassegnato come conveniencequi non fa differenza. Significa solo che Inheritorha un solo inizializzatore designato. Quindi, se ereditiamo Inheritor, dovremmo solo implementare quell'inizializzatore designato e quindi erediteremoInheritor l'inizializzatore di convenienza, il che a sua volta significa che abbiamo implementato tutti gli Baseinizializzatori designati e possiamo ereditare i suoi convenienceinizializzatori.