Существуют две абсолютно важные части специфической для Swift информации, которые отсутствуют в существующих ответах, и я думаю, что это поможет прояснить это полностью.
- Если протокол указывает инициализатор как обязательный метод, этот инициализатор должен быть помечен с использованием
requiredключевого слова Swift .
- Swift имеет специальный набор правил наследования, касающихся
initметодов.
Т.Л., д - р это:
Если вы реализуете какие-либо инициализаторы, вы больше не наследуете ни один из назначенных инициализаторов суперкласса.
Единственные инициализаторы, если таковые имеются, которые вы унаследуете, это удобные инициализаторы суперкласса, которые указывают на назначенный инициализатор, который вы случайно переопределили.
Итак ... готов к длинной версии?
Swift имеет специальный набор правил наследования, касающихся initметодов.
Я знаю, что это был второй из двух моментов, которые я сделал, но мы не можем понять первый пункт, или почему requiredключевое слово вообще существует, пока мы не поймем этот момент. Как только мы поймем этот момент, другой станет довольно очевидным.
Вся информация, которую я рассматриваю в этом разделе этого ответа, взята из документации Apple, найденной здесь .
Из документов Apple:
В отличие от подклассов в Objective-C, подклассы Swift по умолчанию не наследуют свои инициализаторы суперкласса. Подход Swift предотвращает ситуацию, в которой простой инициализатор из суперкласса наследуется более специализированным подклассом и используется для создания нового экземпляра подкласса, который не полностью или правильно инициализирован.
Акцент мой.
Итак, прямо из документации Apple прямо здесь мы видим, что подклассы Swift не всегда (и обычно не) наследуют initметоды своего суперкласса .
Итак, когда они наследуют от своего суперкласса?
Есть два правила, которые определяют, когда подкласс наследует initметоды от своего родителя. Из документов Apple:
Правило 1
Если ваш подкласс не определяет назначенные инициализаторы, он автоматически наследует все назначенные инициализаторы суперкласса.
Правило 2
Если ваш подкласс обеспечивает реализацию всех своих инициализаторов, назначенных суперклассом - либо путем наследования их в соответствии с правилом 1, либо путем предоставления пользовательской реализации как части его определения - тогда он автоматически наследует все удобные инициализаторы суперкласса.
Правило 2 не имеет особого значения для этого разговора , потому что SKSpriteNode«s init(coder: NSCoder)вряд ли будет удобный метод.
Итак, ваш InfoBarкласс наследовал requiredинициализатор вплоть до добавленной вами точки init(team: Team, size: CGSize).
Если бы вы не предоставили этот initметод , и вместо этого сделали свой InfoBar«ы добавлены свойства по желанию или при условии их значения по умолчанию, то вы бы до сих пор был унаследовать SKSpriteNode» S init(coder: NSCoder). Однако, когда мы добавили наш собственный пользовательский инициализатор, мы перестали наследовать назначенные инициализаторы нашего суперкласса (и удобные инициализаторы, которые не указывали на инициализаторы, которые мы реализовали).
Итак, в качестве упрощенного примера я представляю это:
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")
Который представляет следующую ошибку:
Отсутствует аргумент для параметра 'bar' в вызове.

Если бы это был Objective-C, у него не было бы проблем с наследованием. Если бы мы инициализировали Barс initWithFoo:в Objective-C, self.barсвойство было бы просто nil. Это, вероятно, не очень хорошо, но это совершенно правильное состояние для объекта, в котором он находится. Это не совсем корректное состояние для объекта Swift. self.barНе является необязательным и не может быть nil.
Опять же, единственный способ, которым мы наследуем инициализаторы, это не предоставляя свои собственные. Так что, если мы попытаемся наследовать, удалив Bars init(foo: String, bar: String), как таковой:
class Bar: Foo {
var bar: String
}
Теперь мы вернулись к наследованию (вроде), но это не скомпилируется ... и сообщение об ошибке объясняет, почему мы не наследуем initметоды суперкласса :
Проблема: у класса 'Bar' нет инициализаторов
Fix-It: сохраненное свойство 'bar' без инициализаторов предотвращает синтезированные инициализаторы
Если мы добавили хранимые свойства в наш подкласс, то у Swift нет возможности создать действительный экземпляр нашего подкласса с инициализаторами суперкласса, которые не могут знать о сохраненных свойствах нашего подкласса.
Ладно, ну а зачем мне вообще это реализовывать init(coder: NSCoder)? Почему это required?
initМетоды Swift могут играть по специальному набору правил наследования, но соответствие протоколу все еще наследуется по цепочке. Если родительский класс соответствует протоколу, его подклассы должны соответствовать этому протоколу.
Обычно это не проблема, поскольку большинству протоколов требуются только методы, которые не воспроизводятся по специальным правилам наследования в Swift, поэтому, если вы наследуете от класса, соответствующего протоколу, вы также наследуете все методы или свойства, которые позволяют классу удовлетворять требованиям протокола.
Однако помните, что initметоды Swift играют по специальному набору правил и не всегда наследуются. Из-за этого класс, который соответствует протоколу, который требует специальных initметодов (таких как NSCoding), требует, чтобы класс пометил эти initметоды как required.
Рассмотрим этот пример:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
Это не компилируется. Он генерирует следующее предупреждение:
Проблема: Требование инициализатора 'init (foo :)' может быть удовлетворено только 'обязательным' инициализатором в не финальном классе 'ConformingClass'
Fix-It: требуется вставить
Он хочет, чтобы я сделал init(foo: Int)инициализатор требуется. Я мог бы также сделать это счастливым, создав класс final(то есть класс не может быть унаследован от).
Итак, что произойдет, если я подкласс? С этого момента, если я подкласс, я в порядке. Если я добавлю инициализаторы, я вдруг перестану наследовать init(foo:). Это проблематично, потому что теперь я больше не соответствую InitProtocol. Я не могу создать подкласс из класса, который соответствует протоколу, а затем внезапно решить, что я больше не хочу соответствовать этому протоколу. Я унаследовал соответствие протокола, но из-за того, как Swift работает с initнаследованием метода, я не унаследовал часть того, что требуется для соответствия этому протоколу, и я должен его реализовать.
Хорошо, это все имеет смысл. Но почему я не могу получить более полезное сообщение об ошибке?
Возможно, сообщение об ошибке могло бы быть более ясным или лучшим, если бы в нем указывалось, что ваш класс больше не соответствует унаследованному NSCodingпротоколу и что для его исправления необходимо реализовать init(coder: NSCoder). Конечно.
Но Xcode просто не может сгенерировать это сообщение, потому что на самом деле это не всегда будет реальной проблемой, если не реализовать или унаследовать требуемый метод. Существует, по крайней мере, еще одна причина для создания initметодов requiredпомимо соответствия протокола, и это заводские методы.
Если я хочу написать правильный фабричный метод, мне нужно указать тип возвращаемого значения Self(эквивалент Swift Objective-C instanceType). Но для этого мне нужно использовать requiredметод инициализатора.
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
Это генерирует ошибку:
Для создания объекта типа «Self» со значением метатипа необходимо использовать инициализатор required

Это в основном та же проблема. Если мы создадим подкласс Box, наши подклассы унаследуют метод класса factory. Так что мы могли бы позвонить SubclassedBox.factory(). Тем не менее, без requiredключевого слова на init(size:)методе, Boxподклассы «s не гарантируется наследуйте , self.init(size:)что factoryзвонит.
Поэтому мы должны создать этот метод, requiredесли нам нужен такой фабричный метод, а это значит, что если наш класс реализует такой метод, у нас будет requiredметод инициализатора, и мы столкнемся с точно такими же проблемами, с которыми вы столкнулись здесь с NSCodingпротоколом.
В конечном итоге все сводится к базовому пониманию того, что инициализаторы Swift играют по несколько иному набору правил наследования, что означает, что вы не гарантированно наследуете инициализаторы от своего суперкласса. Это происходит потому, что инициализаторы суперкласса не могут знать о ваших новых сохраненных свойствах и не могут создать экземпляр вашего объекта в допустимом состоянии. Но по разным причинам суперкласс может пометить инициализатор как required. Когда это происходит, мы можем либо использовать один из очень специфических сценариев, по которому мы действительно наследуем requiredметод, либо мы должны реализовать его самостоятельно.
Главное здесь - то, что если мы получаем ошибку, которую вы видите здесь, это означает, что ваш класс фактически не реализует метод вообще.
В качестве, возможно, одного из последних примеров того, что подклассы Swift не всегда наследуют initметоды их родителей (что, я думаю, является абсолютно важным для полного понимания этой проблемы), рассмотрим этот пример:
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)
Это не в состоянии скомпилировать.

Это сообщение об ошибке немного вводит в заблуждение:
Дополнительный аргумент 'b' в вызове
Но дело в том, Barне наследует любого из Foo«s initметодов , так как он не удовлетворен либо из двух специальных случаев для наследования initметодов из родительского класса.
Если бы это был Objective-C, мы бы унаследовали это initбез проблем, потому что Objective-C совершенно счастлив, не инициализируя свойства объектов (хотя как разработчик, вы не должны были бы быть довольны этим). В Swift это просто не подойдет. Вы не можете иметь недопустимое состояние, и наследование инициализаторов суперкласса может привести только к недопустимым состояниям объекта.
init(collection:MPMediaItemCollection). Вы должны предоставить реальную коллекцию медиа-предметов; это точка этого класса. Этот класс просто невозможно создать без него. Он собирается проанализировать коллекцию и инициализировать дюжину переменных экземпляра. В этом вся суть единственного назначенного инициализатора! Таким образом, неinit(coder:)имеет никакого значимого (или даже бессмысленного) MPMediaItemCollection для предоставления здесь; толькоfatalErrorподход правильный.