Ci sono due pezzi assolutamente cruciali di informazioni specifiche su Swift che mancano nelle risposte esistenti che penso aiutino a chiarire completamente.
- Se un protocollo specifica un inizializzatore come metodo richiesto, tale inizializzatore deve essere contrassegnato utilizzando la
requiredparola chiave Swift .
- Swift ha un set speciale di regole di ereditarietà relative ai
initmetodi.
Il tl; dr è questo:
Se si implementano inizializzatori, non si eredita più nessuno degli inizializzatori designati della superclasse.
Gli unici inizializzatori, se presenti, che erediterai, sono inizializzatori di convenienza di classe superiore che puntano a un inizializzatore designato che ti è capitato di ignorare.
Quindi ... pronto per la versione lunga?
Swift ha un set speciale di regole di ereditarietà relative ai initmetodi.
So che questo è stato il secondo dei due punti che ho fatto, ma non possiamo capire il primo punto, o perché la requiredparola chiave esiste anche fino a quando non capiamo questo punto. Una volta compreso questo punto, l'altro diventa piuttosto ovvio.
Tutte le informazioni che copro in questa sezione di questa risposta provengono dalla documentazione di Apple trovata qui .
Dai documenti Apple:
A differenza delle sottoclassi in Objective-C, le sottoclassi Swift non ereditano i loro inizializzatori di superclasse per impostazione predefinita. L'approccio di Swift evita una situazione in cui un semplice inizializzatore da una superclasse viene ereditato da una sottoclasse più specializzata e viene utilizzato per creare una nuova istanza della sottoclasse che non è inizializzata completamente o correttamente.
Enfasi mia.
Quindi, direttamente dai documenti di Apple proprio lì, vediamo che le sottoclassi di Swift non erediteranno sempre (e di solito non lo fanno) i initmetodi della loro superclasse .
Quindi, quando ereditano dalla loro superclasse?
Esistono due regole che definiscono quando una sottoclasse eredita i initmetodi dal suo genitore. Dai documenti di Apple:
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.
Regola 2 non è particolarmente rilevante per questa conversazione perché SKSpriteNodes' init(coder: NSCoder)è improbabile che sia un metodo comodo.
Quindi, la tua InfoBarclasse stava ereditando l' requiredinizializzatore fino al punto che hai aggiunto init(team: Team, size: CGSize).
Se si dovesse non hanno fornito questo initmetodo e invece effettuato le InfoBar'proprietà s aggiunto opzionale o fornito loro i valori di default, allora avresti ancora stato eredita SKSpriteNode' s init(coder: NSCoder). Tuttavia, quando abbiamo aggiunto il nostro inizializzatore personalizzato, abbiamo smesso di ereditare gli inizializzatori designati della nostra superclasse (e inizializzatori di convenienza che non puntavano agli inizializzatori che abbiamo implementato).
Quindi, come esempio semplicistico, vi presento questo:
class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}
class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}
let x = Bar(foo: "Foo")
Che presenta il seguente errore:
Argomento mancante per il parametro 'bar' nella chiamata.

Se questo fosse Objective-C, non avrebbe problemi a ereditare. Se inizializzassimo aBar con initWithFoo:in Objective-C, la self.barproprietà sarebbe semplicemente nil. Probabilmente non è grande, ma è perfettamente valida di stato per l'oggetto deve essere. E ' non è uno stato perfettamente valida per l'oggetto Swift di essere in. self.barNon è un optional e non può essere nil.
Ancora una volta, l'unico modo in cui ereditiamo gli inizializzatori è non fornire i nostri. Quindi, se cerchiamo di ereditare cancellando Bar's init(foo: String, bar: String), come ad esempio:
class Bar: Foo {
var bar: String
}
Ora siamo tornati a ereditare (una specie di), ma questo non si compila ... e il messaggio di errore spiega esattamente perché non ereditiamo i initmetodi di superclasse :
Problema: la classe "Bar" non ha inizializzatori
Fix-It: la 'barra' delle proprietà memorizzata senza inizializzatori impedisce gli inizializzatori sintetizzati
Se abbiamo aggiunto proprietà memorizzate nella nostra sottoclasse, non esiste alcun modo rapido per creare un'istanza valida della nostra sottoclasse con gli inizializzatori della superclasse che non potrebbero conoscere le proprietà memorizzate della nostra sottoclasse.
Va bene, perché devo implementare init(coder: NSCoder) ? Perché è required?
I initmetodi di Swift possono svolgere un set speciale di regole di ereditarietà, ma la conformità del protocollo è ancora ereditata lungo la catena. Se una classe genitore è conforme a un protocollo, le sue sottoclassi devono essere conformi a quel protocollo.
Di solito, questo non è un problema, poiché la maggior parte dei protocolli richiede solo metodi che non giocano con regole di ereditarietà speciali in Swift, quindi se erediti da una classe conforme a un protocollo, stai anche ereditando tutti i metodi o proprietà che consentono alla classe di soddisfare la conformità del protocollo.
Tuttavia, ricorda, i initmetodi di Swift si basano su un insieme speciale di regole e non sono sempre ereditati. Per questo motivo, una classe conforme a un protocollo che richiede initmetodi speciali (come NSCoding) richiede che la classe contrassegni tali initmetodi come required.
Considera questo esempio:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
Questo non si compila. Genera il seguente avviso:
Problema: il requisito di inizializzazione 'init (foo :)' può essere soddisfatto solo da un inizializzatore 'obbligatorio' nella classe non finale 'ConformingClass'
Fix-It: inserire richiesto
Vuole che renda init(foo: Int)necessario l' inizializzatore. Potrei anche renderlo felice creando la classe final(il che significa che la classe non può essere ereditata).
Quindi, cosa succede se eseguo una sottoclasse? Da questo punto, se faccio una sottoclasse, sto bene. Se aggiungo comunque degli inizializzatori, all'improvviso non eredito più init(foo:). Questo è problematico perché ora non sono più conforme a InitProtocol. Non posso effettuare la sottoclasse da una classe conforme a un protocollo e poi improvvisamente decidere di non voler più conformarmi a quel protocollo. Ho ereditato la conformità del protocollo, ma a causa del modo in cui Swift lavora con l' initereditarietà del metodo, non ho ereditato parte di ciò che è necessario per conformarsi a quel protocollo e devo implementarlo.
Va bene, tutto questo ha senso. Ma perché non riesco a visualizzare un messaggio di errore più utile?
Probabilmente, il messaggio di errore potrebbe essere più chiaro o migliore se specificasse che la classe non era più conforme al NSCodingprotocollo ereditato e che per risolverlo è necessario implementare init(coder: NSCoder). Sicuro.
Ma Xcode semplicemente non può generare quel messaggio perché in realtà non sarà sempre il vero problema con la mancata implementazione o ereditarietà di un metodo richiesto. C'è almeno un altro motivo per rendere i initmetodi requiredoltre alla conformità del protocollo, e questo è metodi di fabbrica.
Se voglio scrivere un metodo factory adeguato, devo specificare il tipo restituito Self(equivalente di Swift di Objective-C instanceType). Ma per fare questo, ho davvero bisogno di usare un requiredmetodo di inizializzazione.
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
Questo genera l'errore:
La costruzione di un oggetto del tipo di classe "Self" con un valore di metatype deve utilizzare un inizializzatore "obbligatorio"

È sostanzialmente lo stesso problema. Se effettuiamo la sottoclasse Box, le nostre sottoclassi erediteranno il metodo class factory. Quindi potremmo chiamare SubclassedBox.factory(). Tuttavia, senza la requiredparola chiave sul init(size:)metodo, Boxle sottoclassi del metodo non sono garantite per ereditare self.init(size:)ciò che factorysta chiamando.
Quindi dobbiamo fare quel metodo requiredse vogliamo un metodo factory come questo, e ciò significa che se la nostra classe implementa un metodo come questo, avremo un requiredmetodo di inizializzazione e incontreremo gli stessi identici problemi che hai incontrato qui con il NSCodingprotocollo.
Alla fine, tutto si riduce alla comprensione di base che gli inizializzatori di Swift giocano secondo un insieme leggermente diverso di regole di ereditarietà, il che significa che non è garantito che tu erediti gli inizializzatori dalla tua superclasse. Ciò accade perché gli inizializzatori di superclasse non possono conoscere le nuove proprietà archiviate e non sono in grado di creare un'istanza dell'oggetto in uno stato valido. Ma, per vari motivi, una superclasse potrebbe contrassegnare un inizializzatore come required. In tal caso, possiamo utilizzare uno degli scenari molto specifici con cui ereditiamo effettivamente il requiredmetodo o dobbiamo implementarlo da soli.
Il punto principale qui è che se stiamo riscontrando l'errore che vedi qui, significa che la tua classe non sta implementando il metodo.
Come forse un ultimo esempio per approfondire il fatto che le sottoclassi di Swift non ereditano sempre i initmetodi dei loro genitori (che penso sia assolutamente centrale per comprendere appieno questo problema), considera questo esempio:
class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}
class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}
let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)
Questo non riesce a compilare.

Il messaggio di errore che fornisce è un po 'fuorviante:
Argomento aggiuntivo "b" in chiamata
Ma il punto è, Barnon eredita nessuna delle Foo's initmetodi, perché non ha soddisfatto nessuno dei due casi particolari per ereditare initi metodi dalla sua classe padre.
Se questo fosse Objective-C, lo erediteremmo initsenza problemi, perché Objective-C è perfettamente felice di non inizializzare le proprietà degli oggetti (anche se come sviluppatore, non avresti dovuto esserlo). In Swift, questo semplicemente non lo farà. Non è possibile avere uno stato non valido ed ereditare gli inizializzatori di superclasse può solo portare a stati di oggetto non validi.
init(collection:MPMediaItemCollection). È necessario fornire una raccolta di articoli multimediali reali; questo è il punto di questa classe. Questa classe semplicemente non può essere istanziata senza una. Analizzerà la raccolta e inizializzerà una dozzina di variabili di istanza. Questo è il punto centrale di questo essere l'unico inizializzatore designato! Pertanto,init(coder:)non ha alcun MPMediaItemCollection significativo (o addirittura insignificante) da fornire qui; solo l'fatalErrorapproccio è giusto.