156 votes

Swift : Une classe n'implémente pas les membres obligatoires de sa superclasse

J'ai donc mis à jour vers Xcode 6 beta 5 aujourd'hui et j'ai remarqué que je recevais des erreurs dans presque toutes mes sous-classes des classes d'Apple.

L'erreur indique :

La classe "x" n'implémente pas les membres obligatoires de sa superclasse.

Voici un exemple que j'ai choisi parce que cette classe est actuellement assez légère et qu'il sera facile de l'afficher.

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)

    }

}

Ma question est donc la suivante : pourquoi est-ce que je reçois cette erreur, et comment puis-je la corriger ? Qu'est-ce que je n'implémente pas ? J'appelle un initialisateur désigné.

127voto

pasta12 Points 1271

D'un employé d'Apple sur les Forums des développeurs :

"Une façon de déclarer au compilateur et au programme construit que l'on ne veut vraiment pas que vous ne voulez pas être compatible avec le NSCoding est de faire quelque chose comme ça :"

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

Si vous savez que vous ne voulez pas être conforme au NSCoding, c'est une option. J'ai adopté cette approche pour une grande partie de mon code SpriteKit, car je sais que je ne le chargerai pas à partir d'un storyboard.


Une autre option que vous pouvez prendre et qui fonctionne plutôt bien est d'implémenter la méthode comme un init de commodité, comme ceci :

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

Notez l'appel à un initialisateur dans self . Cela vous permet de n'avoir à utiliser que des valeurs fictives pour les paramètres, par opposition à toutes les propriétés non optionnelles, tout en évitant de lancer une erreur fatale.


La troisième option consiste bien sûr à implémenter la méthode tout en appelant super, et à initialiser toutes les propriétés non optionnelles. Vous devriez adopter cette approche si l'objet est une vue chargée à partir d'un storyboard :

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

    super.init(coder: aDecoder)
}

56voto

matt Points 60113

Pourquoi cette question a-t-elle été soulevée ? Eh bien, le fait est qu'elle a toujours Il a toujours été important (c'est-à-dire en Objective-C, depuis le jour où j'ai commencé à programmer Cocoa sous Mac OS X 10.0) de gérer les initialisateurs que votre classe n'est pas préparée à gérer. La documentation a toujours été très claire sur vos responsabilités à cet égard. Mais combien d'entre nous ont pris la peine de les remplir, complètement et à la lettre ? Probablement aucun d'entre nous ! Et le compilateur ne les faisait pas respecter ; tout était purement conventionnel.

Par exemple, dans ma sous-classe de contrôleur de vue en Objective-C avec cet initialisateur désigné :

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

...il est crucial que l'on nous transmette une collection réelle d'éléments de média : l'instance ne peut tout simplement pas exister sans cela. Mais je n'ai pas écrit de "stoppeur" pour empêcher quelqu'un de m'initialiser avec une simple base init à la place. I devrait en avoir écrit un (en fait, à proprement parler, j'aurais dû écrire une implémentation de initWithNibName:bundle: l'initialisateur désigné par l'héritage) ; mais j'étais trop paresseux pour m'en préoccuper, car je "savais" que je n'initialiserais jamais incorrectement ma propre classe de cette façon. Cela laissait un trou béant. En Objective-C, quelqu'un peut appelez le "bare-bones init en laissant mes ivars non initialisés, et nous sommes dans le pétrin sans pagaie.

Swift, merveilleusement, me sauve de moi-même dans la plupart des cas. Dès que j'ai traduit cette application en Swift, tout le problème a disparu. Swift crée effectivement un bouchon pour moi ! Si init(collection:MPMediaItemCollection) est le seul initialisateur désigné déclaré dans ma classe, je ne peux pas être initialisé en appelant la fonction bare-bones init() . C'est un miracle !

Ce qui s'est passé dans la graine 5 est simplement que le compilateur a réalisé que le miracle ne fonctionne pas dans le cas de init(coder:) car en théorie, une instance de cette classe pourrait provenir d'une nib, et le compilateur ne peut pas l'empêcher - et lorsque la nib se charge, init(coder:) sera appelé. Le compilateur vous oblige donc à écrire le stopper explicitement. Et il a raison.

33voto

Gagan Singh Points 397

Ajouter

required init(coder aDecoder: NSCoder!) {
  super.init(coder: aDecoder)
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X