Класс не реализует требуемые члены суперкласса

Поэтому я обновился до Xcode 6 beta 5 сегодня и заметил, что я получил ошибки почти во всех моих подклассах классов Apple.

Ошибка:

Класс 'x' не выполняет требуемые члены суперкласса

Вот один из примеров, который я выбрал, потому что этот класс в настоящее время довольно легкий, поэтому его легко будет опубликовать.

class InfoBar: SKSpriteNode { //Error message here let team: Team let healthBar: SKSpriteNode init(team: Team, size: CGSize) { self.team = team if self.team == Team.TeamGood { healthBar = SKSpriteNode(color: UIColor.greenColor(), size:size) } else { healthBar = SKSpriteNode(color: UIColor.redColor(), size:size) } super.init(texture:nil, color: UIColor.darkGrayColor(), size: size) self.addChild(healthBar) } } 

Поэтому мой вопрос: почему я получаю эту ошибку и как ее исправить? Что я не реализую? Я вызываю назначенный инициализатор.

  • Работа с различными разрешениями устройств iOS в SpriteKit
  • Задержка при отображении UIButtons после представления SKScene?
  • Как использовать applicationWillTerminate для определенного SKScene?
  • Как предотвратить перезапуск последовательности SKAction после декодирования?
  • Sprite Kit / Swift Stars Эффект
  • Сохранение сгенерированной SKTexture в файл
  • как получить значок приложения с сенсорным 3D-телефоном, похожую на peek или pop в SpriteKit
  • Пытается использовать только часть SKTexture, используя textureWithRect inTexture
  • 4 Solutions collect form web for “Класс не реализует требуемые члены суперкласса”

    От сотрудника Apple на форумах разработчиков:

    «Способ объявить компилятору и встроенную программу, которую вы действительно не хотите совместимы с NSCoding, – это сделать что-то вроде этого:

     required init(coder: NSCoder) { fatalError("NSCoding not supported") } 

    Если вы знаете, что не хотите быть совместимым с NSCoding, это вариант. Я использовал этот подход с большим количеством кода SpriteKit, так как я знаю, что не буду загружать его из раскадровки.


    Еще один вариант, который вы можете использовать, который работает достаточно хорошо, – это реализовать метод как удобный init, например:

     convenience required init(coder: NSCoder) { self.init(stringParam: "", intParam: 5) } 

    Обратите внимание на вызов инициализатора в self . Это позволяет вам использовать только фиктивные значения для параметров, в отличие от всех необязательных свойств, избегая при этом роковой ошибки.


    Конечно, третий вариант – реализовать метод при вызове super и инициализировать все ваши необязательные свойства. Вы должны использовать этот подход, если объект представляет собой представление, загружаемое из раскадровки:

     required init(coder aDecoder: NSCoder!) { foo = "some string" bar = 9001 super.init(coder: aDecoder) } 

    Есть две абсолютно важные части информации, зависящей от Swift, которые отсутствуют в существующих ответах, которые, как мне кажется, полностью устраняют это.

    1. Если протокол указывает инициализатор как необходимый метод, этот инициализатор должен быть отмечен с помощью required ключевого слова Swift.
    2. Swift имеет специальный набор правил наследования в отношении методов init .

    Tl; dr – это:

    Если вы реализуете какие-либо инициализаторы, вы больше не наследуете ни один из назначенных инициализаторов суперкласса.

    Единственные инициализаторы, если таковые имеются, которые вы наследуете, являются инициализаторами удобства суперкласса, которые указывают на назначенный инициализатор, который вы переопределили.

    Итак … готовы к длинной версии?


    Swift имеет специальный набор правил наследования в отношении методов init .

    Я знаю, что это был второй из двух пунктов, которые я сделал, но мы не можем понять первый момент или почему required ключевое слово существует даже до тех пор, пока мы не поймем этот момент. Как только мы поймем этот момент, другой становится довольно очевидным.

    Вся информация, которую я расскажу в этом разделе этого ответа, приведена в документации Apple.

    Из документов Apple:

    В отличие от подклассов Objective-C подклассы Swift по умолчанию не наследуют их инициализаторы суперкласса. Подход Swift предотвращает ситуацию, когда простой инициализатор из суперкласса наследуется более специализированным подклассом и используется для создания нового экземпляра подкласса, который не полностью или правильно инициализирован.

    Акцент мой.

    Итак, прямо из документов Apple прямо там, мы видим, что подклассы Swift не всегда (и обычно не наследуют) их методы init .

    Итак, когда они наследуют свой суперкласс?

    Существует два правила, которые определяют, когда подкласс наследует методы init от его родителя. Из документов Apple:

    Правило 1

    Если ваш подкласс не определяет какие-либо назначенные инициализаторы, он автоматически наследует все его инициализаторы, назначенные суперклассам.

    Правило 2

    Если ваш подкласс обеспечивает реализацию всех его инициализаторов, назначенных суперклассам, либо путем наследования их в соответствии с правилом 1, либо путем предоставления пользовательской реализации как части определения, то он автоматически наследует все инициализаторы удобства суперкласса.

    Правило 2 не имеет особого отношения к этому разговору, поскольку SKSpriteNode init(coder: NSCoder) вряд ли будет удобной.

    Таким образом, ваш класс InfoBar наследовал required инициализатор вплоть до момента, когда вы добавили init(team: Team, size: CGSize) .

    Если бы вы не предоставили этот метод init и вместо этого добавили дополнительные свойства InfoBar или предоставили им значения по умолчанию, вы бы по-прежнему наследовали 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, у него не было бы проблем наследования. Если мы инициализировали self.bar с initWithFoo: в Objective-C свойство self.bar просто было бы nil . Вероятно, это не очень хорошо, но это идеальное состояние для объекта. Это не вполне корректное состояние для объекта Swift. self.bar не является необязательным и не может быть nil .

    Опять же, единственный способ унаследовать инициализаторы – это не предоставление наших собственных. Поэтому, если мы попытаемся наследовать, удалив init(foo: String, bar: String) Bar :

     class Bar: Foo { var bar: String } 

    Теперь мы вернемся к наследованию (вроде), но это не скомпилируется … и сообщение об ошибке объясняет, почему мы не наследуем методы суперкласса init :

    Проблема: класс «Бар» не имеет инициализаторов

    Fix-It: сохраненное свойство «bar» без инициализаторов предотвращает синтезированные инициализаторы

    Если мы добавили хранимые свойства в наш подкласс, не существует способа Быстрого создания действительного экземпляра нашего подкласса с инициализаторами суперкласса, которые не могли бы знать о хранимых свойствах нашего подкласса.


    Хорошо, хорошо, почему мне нужно вообще реализовать 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»

    Исправлено: вставить обязательно

    Он хочет, чтобы я сделал init(foo: Int) . Я также мог бы доставить удовольствие, сделав класс final (что означает, что класс не может быть унаследован).

    Итак, что произойдет, если я подкласс? С этого момента, если я подкласс, я в порядке. Однако, если я добавлю какие-либо инициализаторы, я внезапно перестаю наследовать init(foo:) . Это проблематично, потому что теперь я больше не согласуюсь с InitProtocol . Я не могу подкласса из класса, который соответствует протоколу, а затем вдруг решил, что я больше не хочу соответствовать этому протоколу. Я унаследовал соответствие протокола, но из-за того, как Swift работает с наследованием метода init , я не унаследовал часть того, что требуется для соответствия этому протоколу, и я должен его реализовать.


    Хорошо, все это имеет смысл. Но почему я не могу получить более полезное сообщение об ошибке?

    Возможно, сообщение об ошибке может быть более ясным или лучше, если бы он указал, что ваш класс больше не соответствует унаследованному протоколу NSCoding , и что для его исправления вам необходимо реализовать init(coder: NSCoder) . Конечно.

    Но Xcode просто не может сгенерировать это сообщение, потому что это на самом деле не всегда будет реальной проблемой, не реализуя или не наследуя требуемый метод. Существует, по крайней мере, еще одна причина для создания методов init required помимо соответствия протокола, и это фабричные методы.

    Если я хочу написать правильный заводский метод, мне нужно указать тип возврата как « Self (эквивалент Swift для instanceType Objective-C instanceType ). Но для этого мне действительно нужно использовать required метод инициализации.

     class Box { var size: CGSize init(size: CGSize) { self.size = size } class func factory() -> Self { return self.init(size: CGSizeZero) } } 

    Это порождает ошибку:

    Построение объекта типа класса «Я» с значением метатипа должно использовать «обязательный» инициализатор

    введите описание изображения здесь

    Это в основном та же проблема. Если мы подклассифицируем Box , наши подклассы наследуют factory методов класса. Поэтому мы могли бы вызвать SubclassedBox.factory() . Однако без required ключевого слова в методе init(size:) подклассы Box не гарантируют наследование 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 не наследует ни один из методов init Foo потому что он не удовлетворил ни один из двух особых случаев для наследования методов init из его родительского класса.

    Если бы это был Objective-C, мы бы наследовали этот init без проблем, потому что Objective-C совершенно счастлив не инициализировать свойства объектов (хотя, как разработчик, вы не должны были быть довольны этим). В Swift это просто не будет. Вы не можете иметь недопустимое состояние, и наследование инициализаторов суперкласса может привести только к недействительным состояниям объектов.

    Почему эта проблема возникла? Ну, простой факт заключается в том, что он всегда был важным (то есть в Objective-C, с того дня, как я начал программировать Cocoa в Mac OS X 10.0), чтобы иметь дело с инициализаторами, которые ваш класс не готов обрабатывать. В документах всегда были четко указаны ваши обязанности в этом отношении. Но сколько из нас потрудилось выполнить их, полностью и в письме? Наверное, никто из нас! И компилятор не применял их; все это было чисто условно.

    Например, в моем подклассе контроллера Objective-C с этим назначенным инициализатором:

     - (instancetype) initWithCollection: (MPMediaItemCollection*) coll; 

    … очень важно, чтобы нам передали фактическую коллекцию элементов мультимедиа: экземпляр просто не может возникнуть без него. Но я не написал «пробку», чтобы кто-то не инициализировал меня с помощью bare-bones init . Я должен был написать один (собственно говоря, я должен был написать реализацию initWithNibName:bundle: унаследованный назначенный инициализатор); но я был слишком ленив, чтобы беспокоиться, потому что я «знал», что я никогда не ошибочно не инициализировал свой класс таким образом. Это оставило зияющую дыру. В Objective-C кто-то может называть bare-bones init , оставив мои иварины неинициализированными, и мы добираемся до ручья без весла.

    Быстро, чудесно, спасает меня от себя в большинстве случаев. Как только я перевел это приложение в Свифт, вся проблема исчезла. Swift эффективно создает пробку для меня! Если init(collection:MPMediaItemCollection) является единственным назначенным инициализатором, объявленным в моем классе, я не могу быть инициализирован вызовом bare-bones init() . Это чудо!

    То, что произошло в семенах 5, просто состоит в том, что компилятор понял, что чудо не работает в случае init(coder:) , потому что теоретически экземпляр этого класса может исходить из nib, а компилятор не может предотвратить что – и когда загружаются пики, будет вызываться init(coder:) . Поэтому компилятор заставляет вас писать пробку явно. И совершенно верно.

    Добавить

     required init(coder aDecoder: NSCoder!) { super.init(coder: aDecoder) } 
    PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.